From ef12c8ea2860e4bbbdb04da3b5ac6dd7189d6651 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 28 Jul 2023 22:45:25 +0000 Subject: [PATCH 01/15] GUACAMOLE-1841: Create a Cygwin build option. --- configure.ac | 54 ++- src/common-ssh/Makefile.am | 3 +- src/guacd/Makefile.am | 6 + src/guacd/connection.c | 233 +++++++++++- src/guacd/connection.h | 17 + src/guacd/log.c | 19 +- src/guacd/move-pipe.c | 108 ++++++ src/guacd/move-pipe.h | 74 ++++ src/guacd/proc.c | 74 +++- src/guacenc/encode.c | 14 +- src/guaclog/interpret.c | 13 +- src/libguac/Makefile.am | 24 +- src/libguac/client.c | 2 +- src/libguac/error.c | 242 ++++++++++-- src/libguac/guacamole/error-types.h | 12 + src/libguac/guacamole/error.h | 44 +-- src/libguac/guacamole/handle-helpers.h | 83 +++++ src/libguac/{ => guacamole}/id.h | 6 + src/libguac/guacamole/plugin-constants.h | 18 + src/libguac/guacamole/socket-handle.h | 50 +++ src/libguac/guacamole/wait-handle.h | 44 +++ src/libguac/handle-helpers.c | 103 ++++++ src/libguac/id.c | 14 +- src/libguac/socket-handle.c | 451 +++++++++++++++++++++++ src/libguac/tests/id/generate.c | 2 +- src/libguac/user-handshake.c | 16 +- src/libguac/user.c | 2 +- src/libguac/wait-handle.c | 67 ++++ src/protocols/kubernetes/Makefile.am | 3 +- src/protocols/rdp/Makefile.am | 10 +- src/protocols/rdp/channels/rdpgfx.c | 11 + src/protocols/ssh/Makefile.am | 4 +- src/protocols/telnet/Makefile.am | 3 +- src/protocols/vnc/Makefile.am | 3 +- src/pulse/Makefile.am | 3 +- src/terminal/Makefile.am | 3 +- src/terminal/color-scheme.c | 18 + src/terminal/named-colors.c | 9 + 38 files changed, 1729 insertions(+), 133 deletions(-) create mode 100755 src/guacd/move-pipe.c create mode 100755 src/guacd/move-pipe.h create mode 100755 src/libguac/guacamole/handle-helpers.h rename src/libguac/{ => guacamole}/id.h (89%) create mode 100755 src/libguac/guacamole/socket-handle.h create mode 100644 src/libguac/guacamole/wait-handle.h create mode 100755 src/libguac/handle-helpers.c create mode 100644 src/libguac/socket-handle.c create mode 100644 src/libguac/wait-handle.c diff --git a/configure.ac b/configure.ac index d4db3b6d7..6d3a56398 100644 --- a/configure.ac +++ b/configure.ac @@ -40,9 +40,28 @@ AC_PROG_LIBTOOL # Headers AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h time.h sys/time.h syslog.h unistd.h cairo/cairo.h pngstruct.h]) +# Cygwin build +CYWGIN_LDFLAGS= +AC_ARG_WITH([cygwin], + [AS_HELP_STRING([--with-cygwin], + [use Cygwin to build under Windows @<:@default=no@:>@])], + [with_cygwin=yes], [with_cygwin=no]) + +AM_CONDITIONAL([CYGWIN_BUILD], [test "x${with_cygwin}" = "xyes"]) + +if test "x$with_cygwin" = "xyes" +then + CYWGIN_LDFLAGS=-no-undefined + AC_DEFINE([CYGWIN_BUILD],,[Build against Cygwin on Windows]) +fi + # Source characteristics AC_DEFINE([_XOPEN_SOURCE], [700], [Uses X/Open and POSIX APIs]) -AC_DEFINE([__BSD_VISIBLE], [1], [Uses BSD-specific APIs (if available)]) + +if test "x$with_cygwin" = "xno" +then + AC_DEFINE([__BSD_VISIBLE], [1], [Uses BSD-specific APIs (if available)]) +fi # Check for whether math library is required AC_CHECK_LIB([m], [cos], @@ -131,6 +150,7 @@ AC_SUBST(CAIRO_LIBS) AC_SUBST(PTHREAD_LIBS) AC_SUBST(UUID_LIBS) AC_SUBST(CUNIT_LIBS) +AC_SUBST(CYWGIN_LDFLAGS) # Library functions AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset select strdup nanosleep]) @@ -357,20 +377,24 @@ AC_SUBST(SSL_LIBS) have_winsock=disabled WINSOCK_LIBS= -AC_ARG_WITH([winsock], - [AS_HELP_STRING([--with-winsock], - [support Windows Sockets API @<:@default=check@:>@])], - [], - [with_winsock=check]) -if test "x$with_winsock" != "xno" +if test "x$with_cygwin" = "xno" then - have_winsock=yes - AC_CHECK_LIB([wsock32], [main], - [WINSOCK_LIBS="-lwsock32"] - [AC_DEFINE([ENABLE_WINSOCK],, - [Whether Windows Socket API support is enabled])], - [have_winsock=no]) + AC_ARG_WITH([winsock], + [AS_HELP_STRING([--with-winsock], + [support Windows Sockets API @<:@default=check@:>@])], + [], + [with_winsock=check]) + + if test "x$with_winsock" != "xno" + then + have_winsock=yes + AC_CHECK_LIB([wsock32], [main], + [WINSOCK_LIBS="-lwsock32"] + [AC_DEFINE([ENABLE_WINSOCK],, + [Whether Windows Socket API support is enabled])], + [have_winsock=no]) + fi fi AM_CONDITIONAL([ENABLE_WINSOCK], [test "x${have_winsock}" = "xyes"]) @@ -513,7 +537,7 @@ then # Whether libvncserver was built against libgcrypt AC_CHECK_DECL([LIBVNCSERVER_WITH_CLIENT_GCRYPT], - [AC_CHECK_HEADER(gcrypt.h,, + [AC_CHECK_HEADER(gcrypt.h, [VNC_LIBS="$VNC_LIBS -lgcrypt"], [AC_MSG_WARN([ -------------------------------------------- libvncserver appears to be built against @@ -940,7 +964,7 @@ then # Whether libssh2 was built against libgcrypt AC_CHECK_LIB([ssh2], [gcry_control], [AC_CHECK_HEADER(gcrypt.h, - [AC_DEFINE([LIBSSH2_USES_GCRYPT],, + [AC_DEFINE([LIBSSH2_USES_GCRYPT],[SSH_LIBS="$SSH_LIBS -lgcrypt"], [Whether libssh2 was built against libgcrypt])], [AC_MSG_WARN([ -------------------------------------------- diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am index 5d1a88d15..50abb2c13 100644 --- a/src/common-ssh/Makefile.am +++ b/src/common-ssh/Makefile.am @@ -54,5 +54,6 @@ libguac_common_ssh_la_LIBADD = \ libguac_common_ssh_la_LDFLAGS = \ @PTHREAD_LIBS@ \ @SSH_LIBS@ \ - @SSL_LIBS@ + @SSL_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index 356f72f23..42a445845 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -53,6 +53,12 @@ guacd_SOURCES = \ proc.c \ proc-map.c +# Cygwin build +if CYGWIN_BUILD +noinst_HEADERS += move-pipe.h +guacd_SOURCES += move-pipe.c +endif + guacd_CFLAGS = \ -Werror -Wall -pedantic \ @COMMON_INCLUDE@ \ diff --git a/src/guacd/connection.c b/src/guacd/connection.c index 1bb99dc08..f0ecf9772 100644 --- a/src/guacd/connection.c +++ b/src/guacd/connection.c @@ -21,12 +21,12 @@ #include "connection.h" #include "log.h" -#include "move-fd.h" #include "proc.h" #include "proc-map.h" #include #include +#include #include #include #include @@ -45,6 +45,58 @@ #include #include +#ifdef CYGWIN_BUILD + +#include +#include +#include + +#include +#include + +#include "move-pipe.h" + +/** + * Behaves exactly as write(), but writes as much as possible, returning + * successfully only if the entire buffer was written. If the write fails for + * any reason, a negative value is returned. + * + * @param handle + * The file handle to write to. + * + * @param buffer + * The buffer containing the data to be written. + * + * @param length + * The number of bytes in the buffer to write. + * + * @return + * The number of bytes written, or -1 if an error occurs. As this function + * is guaranteed to write ALL bytes, this will always be the number of + * bytes specified by length unless an error occurs. + */ +static int __write_all(HANDLE handle, char* buffer, int length) { + + /* Repeatedly write to the handle until all data is written */ + while (length > 0) { + + DWORD written; + if (guac_write_to_handle(handle, buffer, length, &written)) + return -1; + + length -= written; + buffer += written; + + } + + return length; + +} + +#else + +#include "move-fd.h" + /** * Behaves exactly as write(), but writes as much as possible, returning * successfully only if the entire buffer was written. If the write fails for @@ -83,10 +135,12 @@ static int __write_all(int fd, char* buffer, int length) { } +#endif + /** * Continuously reads from a guac_socket, writing all data read to a file - * descriptor. Any data already buffered from that guac_socket by a given - * guac_parser is read first, prior to reading further data from the + * descriptor or handle. Any data already buffered from that guac_socket by a + * given guac_parser is read first, prior to reading further data from the * guac_socket. The provided guac_parser will be freed once its buffers have * been emptied, but the guac_socket will not. * @@ -111,7 +165,13 @@ static void* guacd_connection_write_thread(void* data) { /* Read all buffered data from parser first */ while ((length = guac_parser_shift(params->parser, buffer, sizeof(buffer))) > 0) { + +#ifdef CYGWIN_BUILD + if (__write_all(params->handle, buffer, length) < 0) +#else if (__write_all(params->fd, buffer, length) < 0) +#endif + break; } @@ -120,7 +180,13 @@ static void* guacd_connection_write_thread(void* data) { /* Transfer data from file descriptor to socket */ while ((length = guac_socket_read(params->socket, buffer, sizeof(buffer))) > 0) { + +#ifdef CYGWIN_BUILD + if (__write_all(params->handle, buffer, length) < 0) +#else if (__write_all(params->fd, buffer, length) < 0) +#endif + break; } @@ -133,24 +199,49 @@ void* guacd_connection_io_thread(void* data) { guacd_connection_io_thread_params* params = (guacd_connection_io_thread_params*) data; char buffer[8192]; - int length; - pthread_t write_thread; pthread_create(&write_thread, NULL, guacd_connection_write_thread, params); +#ifdef CYGWIN_BUILD + + /* Transfer data from file handle to socket */ + while (1) { + + DWORD bytes_read; + if (guac_read_from_handle(params->handle, buffer, sizeof(buffer), &bytes_read)) + break; + + if (guac_socket_write(params->socket, buffer, bytes_read)) + break; + + guac_socket_flush(params->socket); + + } + +#else + /* Transfer data from file descriptor to socket */ + int length; while ((length = read(params->fd, buffer, sizeof(buffer))) > 0) { if (guac_socket_write(params->socket, buffer, length)) break; guac_socket_flush(params->socket); } +#endif + /* Wait for write thread to die */ pthread_join(write_thread, NULL); /* Clean up */ guac_socket_free(params->socket); + +#ifdef CYGWIN_BUILD + CloseHandle(params->handle); +#else close(params->fd); +#endif + free(params); return NULL; @@ -182,6 +273,129 @@ void* guacd_connection_io_thread(void* data) { */ static int guacd_add_user(guacd_proc* proc, guac_parser* parser, guac_socket* socket) { +#ifdef CYGWIN_BUILD + + SECURITY_ATTRIBUTES attributes = { 0 }; + attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + + /* + * Attempt to create a Windows security descriptor that grants access only + * to the owner of this process. + */ + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + + /* + * An SDDL string that uses DACL to grant the General Access (GA) + * permission, only to the owner (OW). For more, see + * https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format. + */ + "D:P(A;;GA;;;OW)", + SDDL_REVISION_1, + + /* The populated security descriptor output */ + &(attributes.lpSecurityDescriptor), + + /* There's no need to capture the descriptor size */ + NULL + + )) { + guacd_log(GUAC_LOG_ERROR, "Unable to initialize named pipe security descriptor."); + return 1; + } + + char pipe_name[GUAC_PIPE_NAME_LENGTH]; + + /* Required pipe name prefix */ + memcpy(pipe_name, PIPE_NAME_PREFIX, strlen(PIPE_NAME_PREFIX)); + + /* UUID to ensure the pipe name is unique */ + char* uuid = guac_generate_id('G'); + if (uuid == NULL) { + guacd_log(GUAC_LOG_ERROR, "Unable to generate UUID for pipe name."); + return 1; + } + + memcpy(pipe_name + strlen(PIPE_NAME_PREFIX), uuid, GUAC_UUID_LEN); + + /* Null terminator */ + pipe_name[GUAC_PIPE_NAME_LENGTH - 1] = '\0'; + + /* + * Set up a named pipe for communication with the user. For more, see + * https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea + */ + HANDLE pipe_handle = CreateNamedPipe( + pipe_name, + + /* + * Read/write and "overlapped" (async) modes. PIPE_WAIT ensures + * that completion actions do not occur until data is actually + * ready, i.e. it's actually possible to wait for data. + */ + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + + /* Allow only one instance of this named pipe to be opened. + * PIPE_WAIT ensures that completion actions do not occur until data + * is actually ready, i.e. it's actually possible to wait for data. + * Also, allow only connections from the local machine. + */ + PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, + + /* Only this one instance of this named pipe is needed */ + 1, + + /* Output and input buffer sizes */ + 8192, 8192, + + /* Use the default timeout for the unused function WaitNamedPipe() */ + 0, + + /* Set our custom security descriptor to allow only owner usage */ + &attributes + + ); + + LocalFree(attributes.lpSecurityDescriptor); + + if (pipe_handle == INVALID_HANDLE_VALUE) { + guacd_log(GUAC_LOG_ERROR, "Unable to create named pipe for IPC."); + return 1; + } + + /* If pipe creation failed, the error will already have been logged */ + if (pipe_handle == NULL) + return 1; + + /* Send pipe name to process so it can connect to the pipe */ + if (!guacd_send_pipe(proc->fd_socket, pipe_name)) { + CloseHandle(pipe_handle); + guacd_log(GUAC_LOG_ERROR, "Unable to add user."); + return 1; + } + + /* Wait for the other end of the pipe to connect before attempting IO */ + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + OVERLAPPED overlapped = { 0 }; + overlapped.hEvent = event; + ConnectNamedPipe(pipe_handle, &overlapped); + + /* Wait for 1 second for the other end to be connected */ + DWORD result = WaitForSingleObject(event, 1000); + if (result == WAIT_FAILED) { + + /* + * If the wait failed for any reason other than the pipe being + * already connected + */ + if (GetLastError() != ERROR_PIPE_CONNECTED) { + guacd_log(GUAC_LOG_ERROR, "Named pipe connection not established."); + return 1; + } + + } + +#else + int sockets[2]; /* Set up socket pair */ @@ -202,10 +416,17 @@ static int guacd_add_user(guacd_proc* proc, guac_parser* parser, guac_socket* so /* Close our end of the process file descriptor */ close(proc_fd); +#endif + guacd_connection_io_thread_params* params = malloc(sizeof(guacd_connection_io_thread_params)); params->parser = parser; params->socket = socket; + +#ifdef CYGWIN_BUILD + params->handle = pipe_handle; +#else params->fd = user_fd; +#endif /* Start I/O thread */ pthread_t io_thread; @@ -390,7 +611,7 @@ void* guacd_connection_thread(void* data) { #else /* Open guac_socket */ - socket = guac_socket_open(connected_socket_fd); + socket = guac_socket_open(connected_handle); #endif /* Route connection according to Guacamole, creating a new process if needed */ diff --git a/src/guacd/connection.h b/src/guacd/connection.h index 08a75266d..42e09d360 100644 --- a/src/guacd/connection.h +++ b/src/guacd/connection.h @@ -28,6 +28,10 @@ #include #endif +#ifdef CYGWIN_BUILD +#include +#endif + /** * Parameters required by each connection thread. */ @@ -92,12 +96,25 @@ typedef struct guacd_connection_io_thread_params { */ guac_socket* socket; + +#ifdef CYGWIN_BUILD + + /** + * The named pipe handle which is being handled by a guac_socket within the + * connection-specific process. + */ + HANDLE handle; + +#else + /** * The file descriptor which is being handled by a guac_socket within the * connection-specific process. */ int fd; +#endif + } guacd_connection_io_thread_params; /** diff --git a/src/guacd/log.c b/src/guacd/log.c index 81c313b86..9bda2adf0 100644 --- a/src/guacd/log.c +++ b/src/guacd/log.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -117,10 +118,11 @@ void guacd_log_guac_error(guac_client_log_level level, const char* message) { guac_error_message); /* Otherwise just log with standard status string */ - else - guacd_log(level, "%s: %s", - message, - guac_status_string(guac_error)); + else { + char* status_string = guac_status_string(guac_error); + guacd_log(level, "%s: %s", message, status_string); + free(status_string); + } } @@ -140,10 +142,13 @@ void guacd_log_handshake_failure() { "Guacamole protocol violation. Perhaps the version of " "guacamole-client is incompatible with this version of " "guacd?"); - else + else { + + char* status_string = guac_status_string(guac_error); guacd_log(GUAC_LOG_WARNING, - "Guacamole handshake failed: %s", - guac_status_string(guac_error)); + "Guacamole handshake failed: %s", status_string); + free(status_string); + } } diff --git a/src/guacd/move-pipe.c b/src/guacd/move-pipe.c new file mode 100755 index 000000000..4ff901482 --- /dev/null +++ b/src/guacd/move-pipe.c @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "log.h" +#include "move-pipe.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Windows headers */ +#include +#include +#include +#include +#include + +int guacd_send_pipe(int sock, char* pipe_name) { + + /* Assign data buffer */ + struct iovec io_vector[1]; + io_vector[0].iov_base = pipe_name; + io_vector[0].iov_len = GUAC_PIPE_NAME_LENGTH; + + struct msghdr message = {0}; + message.msg_iov = io_vector; + message.msg_iovlen = 1; + + /* Send pipe name */ + return (sendmsg(sock, &message, 0) == GUAC_PIPE_NAME_LENGTH); + +} + +HANDLE guacd_recv_pipe(int sock) { + + /* Assign data buffer */ + char pipe_name[GUAC_PIPE_NAME_LENGTH]; + struct iovec io_vector[1]; + io_vector[0].iov_base = pipe_name; + io_vector[0].iov_len = GUAC_PIPE_NAME_LENGTH; + + struct msghdr message = {0}; + message.msg_iov = io_vector; + message.msg_iovlen = 1; + + /* Receive file descriptor */ + if (recvmsg(sock, &message, 0) == GUAC_PIPE_NAME_LENGTH) { + + /* + * Make sure the value is always null-terminated, even if an invalid + * name was sent. + */ + pipe_name[GUAC_PIPE_NAME_LENGTH - 1] = '\0'; + + return CreateFile( + + pipe_name, + + /* Desired access level */ + GENERIC_READ | GENERIC_WRITE, + + /* Sharing level - do not allow any other usage of this pipe */ + 0, + + /* Default security mode - do not allow child processes to inherir */ + NULL, + + /* Open the existing pipe; don't try to create a new one */ + OPEN_EXISTING, + + /* Open in "overlapped" (async) mode */ + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, + + /* Ignored for existing pipes */ + NULL + ); + + } /* end if recvmsg() success */ + + /* Failed to get the pipe */ + return NULL; + +} + diff --git a/src/guacd/move-pipe.h b/src/guacd/move-pipe.h new file mode 100755 index 000000000..1107a26dd --- /dev/null +++ b/src/guacd/move-pipe.h @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUACD_MOVE_HANDLE_H +#define GUACD_MOVE_HANDLE_H + +#include +#include + +/* + * The required prefix for all pipe names in Windows. + */ +#define PIPE_NAME_PREFIX "\\\\.\\pipe\\" + +/* + * The length of a named pipe as used by guacamole. Every pipe name will consist + * of PIPE_NAME_PREFIX, plus a the length of a UUID as returned from + * guac_generate_id(), plus a null-terminator. + */ +#define GUAC_PIPE_NAME_LENGTH (strlen(PIPE_NAME_PREFIX) + GUAC_UUID_LEN + 1) + +/** + * Sends the given pipe name along the given socket. Returns non-zero on success, + * zero on error, just as a normal call to sendmsg() would. If an error does occur, + * GetLastError() will return the appropriate error. + * + * @param sock + * The file descriptor of an open UNIX domain socket along which the pipe + * name specified by pipe_name should be sent. + * + * @param pipe_name + * The null-terminated name of the pipe to send across the socket. The name + * MUST be GUAC_PIPE_NAME_LENGTH characters long, and end with a null + * terminator. + * + * @return + * Non-zero if the send operation succeeded, zero on error. + */ +int guacd_send_pipe(int sock, char* pipe_name); + +/** + * Waits for a pipe name on the given socket, returning a handle to the client + * end of the named pipe with that name. The pipe name must have been sent via + * guacd_send_pipe_name. If an error occurs, NULL is returned, and GetLastError() + * will return the appropriate error. + * + * @param sock + * The file descriptor of an open UNIX domain socket along which the file + * handle will be sent (by guacd_send_handle()). + * + * @return + * The handle to the client end of the named pipe if the operation succeeded, + * NULL otherwise. + */ +HANDLE guacd_recv_pipe(int sock); + +#endif + diff --git a/src/guacd/proc.c b/src/guacd/proc.c index 9325575da..7ab1db170 100644 --- a/src/guacd/proc.c +++ b/src/guacd/proc.c @@ -43,6 +43,13 @@ #include #include +#ifdef CYGWIN_BUILD +#include "move-pipe.h" +#include "guacamole/socket-handle.h" +#include +#include +#endif + /** * Parameters for the user thread. */ @@ -53,11 +60,22 @@ typedef struct guacd_user_thread_params { */ guacd_proc* proc; +#ifdef CYGWIN_BUILD + + /** + * The file handle for communicating with the joining user. + */ + HANDLE handle; + +#else + /** * The file descriptor of the joining user's socket. */ int fd; +#endif + /** * Whether the joining user is the connection owner. */ @@ -84,9 +102,16 @@ static void* guacd_user_thread(void* data) { guac_client* client = proc->client; /* Get guac_socket for user's file descriptor */ +#ifdef CYGWIN_BUILD + guac_socket* socket = guac_socket_open_handle(params->handle); +#else guac_socket* socket = guac_socket_open(params->fd); - if (socket == NULL) +#endif + + if (socket == NULL) { + free(params); return NULL; + } /* Create skeleton user */ guac_user* user = guac_user_alloc(); @@ -112,6 +137,39 @@ static void* guacd_user_thread(void* data) { } +#ifdef CYGWIN_BUILD + +/** + * Begins a new user connection under a given process, using the given file + * handle. The connection will be managed by a separate and detached thread + * which is started by this function. + * + * @param proc + * The process that the user is being added to. + + * @param handle + * The handle associated with the user's connection to guacd. + * + * @param owner + * Non-zero if the user is the owner of the connection being joined (they + * are the first user to join), or zero otherwise. + */ +static void guacd_proc_add_user(guacd_proc* proc, HANDLE handle, int owner) { + + guacd_user_thread_params* params = malloc(sizeof(guacd_user_thread_params)); + params->proc = proc; + params->handle = handle; + params->owner = owner; + + /* Start user thread */ + pthread_t user_thread; + pthread_create(&user_thread, NULL, guacd_user_thread, params); + pthread_detach(user_thread); + +} + +#else + /** * Begins a new user connection under a given process, using the given file * descriptor. The connection will be managed by a separate and detached thread @@ -142,6 +200,8 @@ static void guacd_proc_add_user(guacd_proc* proc, int fd, int owner) { } +#endif + /** * Forcibly kills all processes within the current process group, including the * current process and all child processes. This function is only safe to call @@ -359,6 +419,16 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) { struct sigaction signal_stop_action = { .sa_handler = signal_stop_handler }; sigaction(SIGINT, &signal_stop_action, NULL); sigaction(SIGTERM, &signal_stop_action, NULL); + +#ifdef CYGWIN_BUILD + + /* Add each received file handle as a new user */ + HANDLE handle; + while ((handle = guacd_recv_pipe(proc->fd_socket)) != NULL) { + + guacd_proc_add_user(proc, handle, owner); + +#else /* Add each received file descriptor as a new user */ int received_fd; @@ -366,6 +436,8 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) { guacd_proc_add_user(proc, received_fd, owner); +#endif + /* Future file descriptors are not owners */ owner = 0; diff --git a/src/guacenc/encode.c b/src/guacenc/encode.c index 3de9d796e..e68b54036 100644 --- a/src/guacenc/encode.c +++ b/src/guacenc/encode.c @@ -72,8 +72,11 @@ static int guacenc_read_instructions(guacenc_display* display, /* Fail on read/parse error */ if (guac_error != GUAC_STATUS_CLOSED) { - guacenc_log(GUAC_LOG_ERROR, "%s: %s", - path, guac_status_string(guac_error)); + + char* status_string = guac_status_string(guac_error); + guacenc_log(GUAC_LOG_ERROR, "%s: %s", path, status_string); + free(status_string); + guac_parser_free(parser); return 1; } @@ -132,8 +135,11 @@ int guacenc_encode(const char* path, const char* out_path, const char* codec, /* Obtain guac_socket wrapping file descriptor */ guac_socket* socket = guac_socket_open(fd); if (socket == NULL) { - guacenc_log(GUAC_LOG_ERROR, "%s: %s", path, - guac_status_string(guac_error)); + + char* status_string = guac_status_string(guac_error); + guacenc_log(GUAC_LOG_ERROR, "%s: %s", path, status_string); + free(status_string); + close(fd); guacenc_display_free(display); return 1; diff --git a/src/guaclog/interpret.c b/src/guaclog/interpret.c index d996c2455..8c35521b0 100644 --- a/src/guaclog/interpret.c +++ b/src/guaclog/interpret.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -69,8 +70,9 @@ static int guaclog_read_instructions(guaclog_state* state, /* Fail on read/parse error */ if (guac_error != GUAC_STATUS_CLOSED) { - guaclog_log(GUAC_LOG_ERROR, "%s: %s", - path, guac_status_string(guac_error)); + char* status_string = guac_status_string(guac_error); + guaclog_log(GUAC_LOG_ERROR, "%s: %s", path, status_string); + free(status_string); guac_parser_free(parser); return 1; } @@ -127,8 +129,11 @@ int guaclog_interpret(const char* path, const char* out_path, bool force) { /* Obtain guac_socket wrapping file descriptor */ guac_socket* socket = guac_socket_open(fd); if (socket == NULL) { - guaclog_log(GUAC_LOG_ERROR, "%s: %s", path, - guac_status_string(guac_error)); + + char* status_string = guac_status_string(guac_error); + guaclog_log(GUAC_LOG_ERROR, "%s: %s", path, status_string); + free(status_string); + close(fd); guaclog_state_free(state); return 1; diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 963644ed8..0ad06676a 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -46,6 +46,7 @@ libguacinc_HEADERS = \ guacamole/error-types.h \ guacamole/fips.h \ guacamole/hash.h \ + guacamole/id.h \ guacamole/layer.h \ guacamole/layer-types.h \ guacamole/object.h \ @@ -79,13 +80,12 @@ libguacinc_HEADERS = \ guacamole/wol.h \ guacamole/wol-constants.h -noinst_HEADERS = \ - id.h \ - encode-jpeg.h \ - encode-png.h \ - palette.h \ - user-handlers.h \ - raw_encoder.h \ +noinst_HEADERS = \ + encode-jpeg.h \ + encode-png.h \ + palette.h \ + user-handlers.h \ + raw_encoder.h \ wait-fd.h libguac_la_SOURCES = \ @@ -137,6 +137,16 @@ libguac_la_SOURCES += socket-wsa.c libguacinc_HEADERS += guacamole/socket-wsa.h endif +# Cygwin build +if CYGWIN_BUILD +libguac_la_SOURCES += handle-helpers.c +libguac_la_SOURCES += socket-handle.c +libguac_la_SOURCES += wait-handle.c +libguacinc_HEADERS += guacamole/handle-helpers.h +libguacinc_HEADERS += guacamole/socket-handle.h +libguacinc_HEADERS += guacamole/wait-handle.h +endif + libguac_la_CFLAGS = \ -Werror -Wall -pedantic diff --git a/src/libguac/client.c b/src/libguac/client.c index d490e6438..b904fb7e4 100644 --- a/src/libguac/client.c +++ b/src/libguac/client.c @@ -34,7 +34,7 @@ #include "guacamole/string.h" #include "guacamole/timestamp.h" #include "guacamole/user.h" -#include "id.h" +#include #include #include diff --git a/src/libguac/error.c b/src/libguac/error.c index 5561b1781..35a9095d1 100644 --- a/src/libguac/error.c +++ b/src/libguac/error.c @@ -29,6 +29,12 @@ #include #endif +#ifdef CYGWIN_BUILD +#include +#include +#include +#endif + /* * Error strings */ @@ -58,109 +64,214 @@ const char* __GUAC_STATUS_REFUSED_STR = "Operation refused"; const char* __GUAC_STATUS_TOO_MANY_STR = "Insufficient resources"; const char* __GUAC_STATUS_WOULD_BLOCK_STR = "Operation would block"; -const char* guac_status_string(guac_status status) { +#ifdef CYGWIN_BUILD + +/* + * A format for a backup message in case Windows can't produce one. + */ +#define FALLBACK_ERROR_FORMAT "Failed to get Windows error for code %ul" + +/* + * The size of newly constructed fallback error buffers. Large enough to + * contain the longest possible DWORD and a null terminator. + */ +#define FALLBACK_ERROR_SIZE 64 + +/** + * Get the error message associated with the most recent Windows error. This + * message is allocated fresh every time this function is called, and must be + * freed by the caller. + * + */ +static LPTSTR get_last_error_message() { + + /* This will be set to point to the buffer allocated by FormatMessage */ + LPTSTR error_buffer = NULL; + + /* Attempt to format message using system error message tables */ + DWORD length = FormatMessage( + + /* Fetch error messages from system message tables */ + FORMAT_MESSAGE_FROM_SYSTEM + + /* Allocate a buffer to hold the message text */ + |FORMAT_MESSAGE_ALLOCATE_BUFFER + + /* We're not supplying our own text inserts */ + |FORMAT_MESSAGE_IGNORE_INSERTS, + + /* Don't pass any custom message tables */ + NULL, + + /* Pass the error code in to get the error message */ + guac_windows_error_code, + + /* Use default language settings */ + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + + /* + * Cast the address of the buffer back to a LPTSTR, even though it's + * really a LPTSTR*. The function will internally treat it as a LPTSTR*, + * populating `errorText` with the message text. + */ + (LPTSTR)&error_buffer, + + /* Minimum output buffer size */ + 0, + + /* No extra arguments */ + NULL + ); + + /* If Windows failed to generate an error message */ + if (!length) { + + /* Allocate and format the fallback error message */ + error_buffer = malloc(FALLBACK_ERROR_SIZE * sizeof(TCHAR)); + + /* Format the message */ + sprintf(error_buffer, FALLBACK_ERROR_FORMAT, guac_windows_error_code); + } + + return error_buffer; + +} + +#endif + +/** + * Allocate and return a copy of the provided null-terminated string constant. + * + * @param string_constant + * The null-terminated string constant to duplicated. + * + * @return + * A newly allocated copy of the provided string constant. + */ +static char* guac_status_duplicate_constant(const char* string_constant) { + + /* Create a buffer to hold the message plus null terminator */ + char* string_copy = malloc((strlen(string_constant) + 1) * sizeof(char)); + + /* Copy the constant into the buffer */ + strcpy(string_copy, string_constant); + + /* A newly-allocated copy of the constant */ + return string_copy; + +} + +char* guac_status_string(guac_status status) { switch (status) { /* No error */ case GUAC_STATUS_SUCCESS: - return __GUAC_STATUS_SUCCESS_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_SUCCESS_STR); /* Out of memory */ case GUAC_STATUS_NO_MEMORY: - return __GUAC_STATUS_NO_MEMORY_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NO_MEMORY_STR); /* End of stream */ case GUAC_STATUS_CLOSED: - return __GUAC_STATUS_CLOSED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_CLOSED_STR); /* Timeout */ case GUAC_STATUS_TIMEOUT: - return __GUAC_STATUS_TIMEOUT_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_TIMEOUT_STR); /* Further information in errno */ case GUAC_STATUS_SEE_ERRNO: - return strerror(errno); + return guac_status_duplicate_constant(strerror(errno)); + +#ifdef CYGWIN_BUILD + + /* Further information available from the Windows API. */ + case GUAC_STATUS_SEE_WINDOWS_ERROR: + return get_last_error_message(); + +#endif /* Input/output error */ case GUAC_STATUS_IO_ERROR: - return __GUAC_STATUS_IO_ERROR_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_IO_ERROR_STR); /* Invalid argument */ case GUAC_STATUS_INVALID_ARGUMENT: - return __GUAC_STATUS_INVALID_ARGUMENT_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_INVALID_ARGUMENT_STR); /* Internal error */ case GUAC_STATUS_INTERNAL_ERROR: - return __GUAC_STATUS_INTERNAL_ERROR_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_INTERNAL_ERROR_STR); /* Out of space */ case GUAC_STATUS_NO_SPACE: - return __GUAC_STATUS_NO_SPACE_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NO_SPACE_STR); /* Input too large */ case GUAC_STATUS_INPUT_TOO_LARGE: - return __GUAC_STATUS_INPUT_TOO_LARGE_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_INPUT_TOO_LARGE_STR); /* Result too large */ case GUAC_STATUS_RESULT_TOO_LARGE: - return __GUAC_STATUS_RESULT_TOO_LARGE_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_RESULT_TOO_LARGE_STR); /* Permission denied */ case GUAC_STATUS_PERMISSION_DENIED: - return __GUAC_STATUS_PERMISSION_DENIED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_PERMISSION_DENIED_STR); /* Resource is busy */ case GUAC_STATUS_BUSY: - return __GUAC_STATUS_BUSY_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_BUSY_STR); /* Resource not available */ case GUAC_STATUS_NOT_AVAILABLE: - return __GUAC_STATUS_NOT_AVAILABLE_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NOT_AVAILABLE_STR); /* Not supported */ case GUAC_STATUS_NOT_SUPPORTED: - return __GUAC_STATUS_NOT_SUPPORTED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NOT_SUPPORTED_STR); /* Not implemented */ case GUAC_STATUS_NOT_INPLEMENTED: - return __GUAC_STATUS_NOT_INPLEMENTED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NOT_INPLEMENTED_STR); /* Temporary failure */ case GUAC_STATUS_TRY_AGAIN: - return __GUAC_STATUS_TRY_AGAIN_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_TRY_AGAIN_STR); /* Guacamole protocol error */ case GUAC_STATUS_PROTOCOL_ERROR: - return __GUAC_STATUS_PROTOCOL_ERROR_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_PROTOCOL_ERROR_STR); /* Resource not found */ case GUAC_STATUS_NOT_FOUND: - return __GUAC_STATUS_NOT_FOUND_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_NOT_FOUND_STR); /* Operation canceled */ case GUAC_STATUS_CANCELED: - return __GUAC_STATUS_CANCELED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_CANCELED_STR); /* Value out of range */ case GUAC_STATUS_OUT_OF_RANGE: - return __GUAC_STATUS_OUT_OF_RANGE_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_OUT_OF_RANGE_STR); /* Operation refused */ case GUAC_STATUS_REFUSED: - return __GUAC_STATUS_REFUSED_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_REFUSED_STR); /* Too many resource in use */ case GUAC_STATUS_TOO_MANY: - return __GUAC_STATUS_TOO_MANY_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_TOO_MANY_STR); /* Operation would block */ case GUAC_STATUS_WOULD_BLOCK: - return __GUAC_STATUS_WOULD_BLOCK_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_WOULD_BLOCK_STR); /* Unknown status code */ default: - return __GUAC_STATUS_UNKNOWN_STATUS_STR; + return guac_status_duplicate_constant(__GUAC_STATUS_UNKNOWN_STATUS_STR); } @@ -218,10 +329,10 @@ guac_status* __guac_error() { } -const char** __guac_error_message() { +char** __guac_error_message() { /* Pointer for thread-local data */ - const char** message; + char** message; /* Init error message key, if not already initialized */ pthread_once( @@ -230,11 +341,11 @@ const char** __guac_error_message() { ); /* Retrieve thread-local message variable */ - message = (const char**) pthread_getspecific(__guac_error_message_key); + message = (char**) pthread_getspecific(__guac_error_message_key); /* Allocate thread-local message variable if not already allocated */ if (message == NULL) { - message = malloc(sizeof(const char*)); + message = malloc(sizeof(char*)); pthread_setspecific(__guac_error_message_key, message); } @@ -242,22 +353,89 @@ const char** __guac_error_message() { } +#ifdef CYGWIN_BUILD + +/** + * PThread key for the thread local Windows error code associated with + * the current guac_status, if the status is GUAC_STATUS_SEE_WINDOWS_ERROR. + */ +static pthread_key_t guac_windows_code_key; + +/** + * A static pthread_once_t instance to ensure that the Windows error code + * thread local is only created once. + */ +static pthread_once_t guac_windows_code_key_init = PTHREAD_ONCE_INIT; + +/** + * Allocate the PThread key for the Windows error code associated with the + * current error. + */ +static void guac_alloc_windows_code_key() { + + /* Create key, destroy any allocated variable on thread exit */ + pthread_key_create(&guac_windows_code_key, __guac_free_pointer); + +} + +DWORD* __guac_windows_code() { + + /* Pointer for thread-local data */ + DWORD* windows_code; + + /* Init error code key, if not already initialized */ + pthread_once(&guac_windows_code_key_init, guac_alloc_windows_code_key); + + /* Get the value of the current thread local */ + windows_code = (DWORD*) pthread_getspecific(guac_windows_code_key); + + /* Allocate thread-local error code variable if not already allocated */ + if (windows_code == NULL) { + windows_code = malloc(sizeof(DWORD)); + pthread_setspecific(guac_windows_code_key, windows_code); + } + + return windows_code; + +} + +#endif + #else /* Default (not-threadsafe) implementation */ static guac_status __guac_error_unsafe_storage; -static const char** __guac_error_message_unsafe_storage; +static char** __guac_error_message_unsafe_storage; guac_status* __guac_error() { return &__guac_error_unsafe_storage; } -const char** __guac_error_message() { +char** __guac_error_message() { return &__guac_error_message_unsafe_storage; } /* Warn about threadsafety */ #warn No threadsafe implementation of __guac_error exists for your platform, so a default non-threadsafe implementation has been used instead. This may lead to incorrect status codes being reported for failures. Please consider adding support for your platform, or filing a bug report with the Guacamole project. +#ifdef CYGWIN_BUILD + +/** + * Non-threadsafe storage for the Windows error code. + */ +static DWORD __guac_windows_error_unsafe_storage; + +/** + * Return non-threadsafe Windows error code. + * + * @return + * The non-threadsafe Windows error code. + */ +DWORD* __guac_windows_code() { + return &__guac_windows_error_unsafe_storage; +} + +#endif + #endif diff --git a/src/libguac/guacamole/error-types.h b/src/libguac/guacamole/error-types.h index 4cf3499ea..7622fb35e 100644 --- a/src/libguac/guacamole/error-types.h +++ b/src/libguac/guacamole/error-types.h @@ -20,6 +20,8 @@ #ifndef _GUAC_ERROR_TYPES_H #define _GUAC_ERROR_TYPES_H +#include "config.h" + /** * Type definitions related to return values and errors. * @@ -57,6 +59,16 @@ typedef enum guac_status { * stored in errno. */ GUAC_STATUS_SEE_ERRNO, + +#ifdef CYGWIN_BUILD + + /** + * An error occurred, and further information about the error can be + * retrieved using the GetLastError function. + */ + GUAC_STATUS_SEE_WINDOWS_ERROR, + +#endif /** * An I/O error prevented the operation from succeeding. diff --git a/src/libguac/guacamole/error.h b/src/libguac/guacamole/error.h index a511d315f..46a4fb38f 100644 --- a/src/libguac/guacamole/error.h +++ b/src/libguac/guacamole/error.h @@ -30,10 +30,15 @@ #include "error-types.h" +#ifdef CYGWIN_BUILD +#include +#endif + /** - * Returns a human-readable explanation of the status code given. + * Returns a newly-allocated, null-terminated, and human-readable explanation + * of the status code given. */ -const char* guac_status_string(guac_status status); +char* guac_status_string(guac_status status); /** * Returns the status code associated with the error which occurred during the @@ -58,37 +63,24 @@ guac_status* __guac_error(); */ #define guac_error_message (*__guac_error_message()) -const char** __guac_error_message(); +char** __guac_error_message(); -/** - * Returns a human-readable explanation of the status code given. - */ -const char* guac_status_string(guac_status status); +#ifdef CYGWIN_BUILD /** - * 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. + * Returns an error code describing the Windows error that occured when + * attempting the Windows function call that induced the guac_error status + * being set to GUAC_STATUS_SEE_WINDOWS_ERROR. This value is meaningless if + * any other guac status is set. * - * 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. + * The storage of this value is thread-local. Assignment of an error code in + * one thread will not affect its value in another thread. */ -#define guac_error (*__guac_error()) +#define guac_windows_error_code (*__guac_windows_code()) -guac_status* __guac_error(); +DWORD* __guac_windows_code(); -/** - * 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. - * - * 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. - */ -#define guac_error_message (*__guac_error_message()) - -const char** __guac_error_message(); +#endif #endif diff --git a/src/libguac/guacamole/handle-helpers.h b/src/libguac/guacamole/handle-helpers.h new file mode 100755 index 000000000..b1f768de1 --- /dev/null +++ b/src/libguac/guacamole/handle-helpers.h @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_HANDLE_HELPERS_H +#define GUAC_HANDLE_HELPERS_H + +#include + +/** + * Attempt to read the provided count of bytes from the provided handle, into the + * provided buffer. If the read is successful, 0 will be returned, and the actual + * number of bytes written to the buffer will be saved to the provided + * num_bytes_read pointer. If an error occurs while attempting to read from the + * handle, or while waiting on the results of the read attempt, the error code + * (as returned by GetLastError()) will be returned. + * + * @param handle + * The handle to read from. This handle MUST have been opened in overlapped + * mode. + * + * @param buffer + * The buffer to write the data into. It must be at least `count` bytes. + * + * @param count + * The maximum number of bytes to read from the handle. + * + * @param num_bytes_read + * The actual number of bytes read from the handle. This value is valid only + * if this function returns successfully. This value may be less than `count`. + * + * @return + * Zero on success, or the failure code (as returned by GetLastError()) if + * the read attempt, or the wait on that read attempt fails. + */ +int guac_read_from_handle( + HANDLE handle, void* buffer, DWORD count, DWORD* num_bytes_read); + +/** + * Attempt to wrtie the provided count of bytes to the provided handle, from the + * provided buffer. If the write is successful, 0 will be returned, and the actual + * number of bytes written to the handle will be saved to the provided + * num_bytes_written pointer. If an error occurs while attempting to write to the + * handle, or while waiting on the results of the write attempt, the error code + * (as returned by GetLastError()) will be returned. + * + * @param handle + * The handle to write to. This handle MUST have been opened in overlapped + * mode. + * + * @param buffer + * The buffer to write to the handle. It must be at least `count` bytes. + * + * @param count + * The maximum numer of bytes to write to the handle. + * + * @param num_bytes_written + * The actual number of bytes written to the handle. This value is valid only + * if this function returns successfully. This value may be less than `count`. + * + * @return + * Zero on success, or the failure code (as returned by GetLastError()) if + * the write attempt, or the wait on that write attempt fails. + */ +int guac_write_to_handle( + HANDLE handle, const void* buffer, DWORD count, DWORD* num_bytes_written); + +#endif \ No newline at end of file diff --git a/src/libguac/id.h b/src/libguac/guacamole/id.h similarity index 89% rename from src/libguac/id.h rename to src/libguac/guacamole/id.h index 9b64d4108..762dc2998 100644 --- a/src/libguac/id.h +++ b/src/libguac/guacamole/id.h @@ -20,6 +20,12 @@ #ifndef __GUAC_ID_H #define __GUAC_ID_H +/** + * The length of a guac UUID in bytes. All UUIDs returned by guac_generate_id() + * are guaranteed to be 37 1-byte characters long. + */ +#define GUAC_UUID_LEN 37 + /** * Generates a guaranteed-unique identifier which is a total of 37 characters * long, having the given single-character prefix. The resulting identifier diff --git a/src/libguac/guacamole/plugin-constants.h b/src/libguac/guacamole/plugin-constants.h index a67f5f54c..7aeef251f 100644 --- a/src/libguac/guacamole/plugin-constants.h +++ b/src/libguac/guacamole/plugin-constants.h @@ -20,12 +20,28 @@ #ifndef _GUAC_PLUGIN_CONSTANTS_H #define _GUAC_PLUGIN_CONSTANTS_H +#include "config.h" + /** * Constants related to client plugins. * * @file plugin-constants.h */ +#ifdef CYGWIN_BUILD + +/** + * String prefix which begins the library filename of all client plugins. + */ +#define GUAC_PROTOCOL_LIBRARY_PREFIX "cygguac-client-" + +/** + * String suffix which ends the library filename of all client plugins. + */ +#define GUAC_PROTOCOL_LIBRARY_SUFFIX "-0.dll" + +#else + /** * String prefix which begins the library filename of all client plugins. */ @@ -36,6 +52,8 @@ */ #define GUAC_PROTOCOL_LIBRARY_SUFFIX ".so" +#endif + /** * The maximum number of characters (COUNTING NULL TERMINATOR) to allow * for protocol names within the library filename of client plugins. diff --git a/src/libguac/guacamole/socket-handle.h b/src/libguac/guacamole/socket-handle.h new file mode 100755 index 000000000..56c3c0c77 --- /dev/null +++ b/src/libguac/guacamole/socket-handle.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_SOCKET_HANDLE_H +#define GUAC_SOCKET_HANDLE_H + +/** + * Provides an implementation of guac_socket specific to the Windows file handle + * API. This header willonly be available if libguac was built with Cgywin. + * + * @file socket-handle.h + */ + +#include "socket-types.h" + +#include + +/** + * Creates a new guac_socket which will use the Windows handle API for all + * communication. Freeing this guac_socket will automatically close the + * associated handle. + * + * @param handle + * The handle to use for the communicating with the connection underlying + * the created guac_socket. + * + * @return + * A newly-allocated guac_socket which will transparently use the Windows + * handle API for all communication. + */ +guac_socket* guac_socket_open_handle(HANDLE handle); + +#endif + diff --git a/src/libguac/guacamole/wait-handle.h b/src/libguac/guacamole/wait-handle.h new file mode 100644 index 000000000..7e135f4b5 --- /dev/null +++ b/src/libguac/guacamole/wait-handle.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_WAIT_HANDLE_H +#define GUAC_WAIT_HANDLE_H + +#include + +/** + * Waits for data to be available for reading on a given file handle. Returns + * zero if data is available, a negative value if the wait timed out without + * data being available, or a positive Windows error code if the wait failed. + * + * @param handle + * The file handle to wait for. + * + * @param usec_timeout + * The maximum number of microseconds to wait for data, or -1 to + * potentially wait forever. + * + * @return + * Zero if data is available for reading, negative if the timeout elapsed + * and no data is available, or a positive Windows error code if an error + * occurs. + */ +int guac_wait_for_handle(HANDLE handle, int usec_timeout); + +#endif diff --git a/src/libguac/handle-helpers.c b/src/libguac/handle-helpers.c new file mode 100755 index 000000000..6c878336f --- /dev/null +++ b/src/libguac/handle-helpers.c @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include "guacamole/error.h" +#include "guacamole/handle-helpers.h" + +#include +#include +#include +#include + +#include + +int guac_read_from_handle( + HANDLE handle, void* buffer, DWORD count, DWORD* num_bytes_read) { + + /* + * Overlapped structure and associated event for waiting on async call. + * The event isn't used by this function, but is required in order to + * reliably wait on an async operation anyway - see + * https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult#remarks. + */ + OVERLAPPED overlapped = { 0 }; + overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + /* Attempt to start the async read operation */ + if (!ReadFile(handle, buffer, count, NULL, &overlapped)) { + + DWORD error = GetLastError(); + + /* + * If an error other than the expected ERROR_IO_PENDING happens, + * return it as the error code immediately. + */ + if (error != ERROR_IO_PENDING) { + return error; + } + + } + + /* + * Wait on the result of the read. If any error occurs when waiting, + * return the error. + */ + if (!GetOverlappedResult(handle, &overlapped, num_bytes_read, TRUE)) + return GetLastError(); + + /* No errors occured, so the read was successful */ + return 0; + +} + +int guac_write_to_handle( + HANDLE handle, const void* buffer, DWORD count, DWORD* num_bytes_written) { + + /* + * Overlapped structure and associated event for waiting on async call. + */ + OVERLAPPED overlapped = { 0 }; + overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + /* Attempt to start the async write operation */ + if (!WriteFile(handle, buffer, count, NULL, &overlapped)) { + + DWORD error = GetLastError(); + + /* + * If an error other than the expected ERROR_IO_PENDING happens, + * return it as the error code immediately. + */ + if (error != ERROR_IO_PENDING) + return error; + + } + + /* + * Wait on the result of the write. If any error occurs when waiting, + * return the error. + */ + if (!GetOverlappedResult(handle, &overlapped, num_bytes_written, TRUE)) + return GetLastError(); + + /* No errors occured, so the write was successful */ + return 0; + +} \ No newline at end of file diff --git a/src/libguac/id.c b/src/libguac/id.c index e627f89d8..610f836d5 100644 --- a/src/libguac/id.c +++ b/src/libguac/id.c @@ -20,7 +20,7 @@ #include "config.h" #include "guacamole/error.h" -#include "id.h" +#include #if defined(HAVE_LIBUUID) #include @@ -32,12 +32,6 @@ #include -/** - * The length of a UUID in bytes. All UUIDs are guaranteed to be 36 1-byte - * characters long. - */ -#define GUAC_UUID_LEN 36 - char* guac_generate_id(char prefix) { char* buffer; @@ -68,7 +62,7 @@ char* guac_generate_id(char prefix) { #endif /* Allocate buffer for future formatted ID */ - buffer = malloc(GUAC_UUID_LEN + 2); + buffer = malloc(GUAC_UUID_LEN + 1); if (buffer == NULL) { #ifndef HAVE_LIBUUID uuid_destroy(uuid); @@ -84,7 +78,7 @@ char* guac_generate_id(char prefix) { #ifdef HAVE_LIBUUID uuid_unparse_lower(uuid, identifier); #else - size_t identifier_length = GUAC_UUID_LEN + 1; + size_t identifier_length = GUAC_UUID_LEN; if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) { free(buffer); uuid_destroy(uuid); @@ -98,7 +92,7 @@ char* guac_generate_id(char prefix) { #endif buffer[0] = prefix; - buffer[GUAC_UUID_LEN + 1] = '\0'; + buffer[GUAC_UUID_LEN] = '\0'; return buffer; } diff --git a/src/libguac/socket-handle.c b/src/libguac/socket-handle.c new file mode 100644 index 000000000..dce0291cc --- /dev/null +++ b/src/libguac/socket-handle.c @@ -0,0 +1,451 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" + +#include "guacamole/error.h" +#include "guacamole/handle-helpers.h" +#include "guacamole/socket.h" +#include "guacamole/wait-handle.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** + * Data associated with an open socket which writes to a file handle. + */ +typedef struct guac_socket_handle_data { + + /** + * The associated file handle. + */ + HANDLE handle; + + /** + * The number of bytes currently in the main write buffer. + */ + int written; + + /** + * The main write buffer. Bytes written go here before being flushed + * to the open file descriptor. + */ + char out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE]; + + /** + * Lock which is acquired when an instruction is being written, and + * released when the instruction is finished being written. + */ + pthread_mutex_t socket_lock; + + /** + * Lock which protects access to the internal buffer of this socket, + * guaranteeing atomicity of writes and flushes. + */ + pthread_mutex_t buffer_lock; + +} guac_socket_handle_data; + +/** + * Writes the entire contents of the given buffer to the file handle + * associated with the given socket, retrying as necessary until the whole + * buffer is written, and aborting if an error occurs. + * + * @param socket + * The guac_socket associated with the file handle to which the given + * buffer should be written. + * + * @param buf + * The buffer of data to write to the given guac_socket. + * + * @param count + * The number of bytes within the given buffer. + * + * @return + * The number of bytes written, which will be exactly the size of the given + * buffer, or a negative value if an error occurs. + */ +static ssize_t guac_socket_handle_write(guac_socket* socket, + const void* buf, size_t count) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + const char* buffer = buf; + + /* Write until completely written */ + while (count > 0) { + + DWORD bytes_written; + + DWORD error = guac_write_to_handle(data->handle, buffer, count, &bytes_written); + + if (error) { + guac_error = GUAC_STATUS_SEE_WINDOWS_ERROR; + guac_windows_error_code = error; + guac_error_message = "Error writing data to handle"; + return -1; + } + + /* Advance buffer to next chunk */ + buffer += bytes_written; + count -= bytes_written; + + } + + return 0; + +} + +/** + * Attempts to read from the underlying file handle of the given + * guac_socket, populating the given buffer. + * + * @param socket + * The guac_socket being read from. + * + * @param buf + * The arbitrary buffer which we must populate with data. + * + * @param count + * The maximum number of bytes to read into the buffer. + * + * @return + * The number of bytes read, or -1 if an error occurs. + */ +static ssize_t guac_socket_handle_read_handler(guac_socket* socket, + void* buf, size_t count) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + DWORD bytes_read; + do { + + DWORD error = guac_read_from_handle(data->handle, buf, count, &bytes_read); + if (error) { + guac_error = GUAC_STATUS_SEE_WINDOWS_ERROR; + guac_windows_error_code = error; + guac_error_message = "Error reading data from handle"; + return -1; + } + + } while(bytes_read == 0); + + return bytes_read; + +} + +/** + * Flushes the contents of the output buffer of the given socket immediately, + * without first locking access to the output buffer. This function must ONLY + * be called if the buffer lock has already been acquired. + * + * @param socket + * The guac_socket to flush. + * + * @return + * Zero if the flush operation was successful, non-zero otherwise. + */ +static ssize_t guac_socket_handle_flush(guac_socket* socket) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Flush remaining bytes in buffer */ + if (data->written > 0) { + + /* Write ALL bytes in buffer immediately */ + if (guac_socket_handle_write(socket, data->out_buf, data->written)) + return 1; + + data->written = 0; + } + + return 0; + +} + +/** + * Flushes the internal buffer of the given guac_socket, writing all data + * to the underlying file handle. + * + * @param socket + * The guac_socket to flush. + * + * @return + * Zero if the flush operation was successful, non-zero otherwise. + */ +static ssize_t guac_socket_handle_flush_handler(guac_socket* socket) { + + int retval; + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Acquire exclusive access to buffer */ + pthread_mutex_lock(&(data->buffer_lock)); + + /* Flush contents of buffer */ + retval = guac_socket_handle_flush(socket); + + /* Relinquish exclusive access to buffer */ + pthread_mutex_unlock(&(data->buffer_lock)); + + return retval; + +} + +/** + * Writes the contents of the buffer to the output buffer of the given socket, + * flushing the output buffer as necessary, without first locking access to the + * output buffer. This function must ONLY be called if the buffer lock has + * already been acquired. + * + * @param socket + * The guac_socket to write the given buffer to. + * + * @param buf + * The buffer to write to the given socket. + * + * @param count + * The number of bytes in the given buffer. + * + * @return + * The number of bytes written, or a negative value if an error occurs + * during write. + */ +static ssize_t guac_socket_handle_write_buffered(guac_socket* socket, + const void* buf, size_t count) { + + size_t original_count = count; + const char* current = buf; + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Append to buffer, flush if necessary */ + while (count > 0) { + + int chunk_size; + int remaining = sizeof(data->out_buf) - data->written; + + /* If no space left in buffer, flush and retry */ + if (remaining == 0) { + + /* Abort if error occurs during flush */ + if (guac_socket_handle_flush(socket)) + return -1; + + /* Retry buffer append */ + continue; + + } + + /* Calculate size of chunk to be written to buffer */ + chunk_size = count; + if (chunk_size > remaining) + chunk_size = remaining; + + /* Update output buffer */ + memcpy(data->out_buf + data->written, current, chunk_size); + data->written += chunk_size; + + /* Update provided buffer */ + current += chunk_size; + count -= chunk_size; + + } + + /* All bytes have been written, possibly some to the internal buffer */ + return original_count; + +} + +/** + * Appends the provided data to the internal buffer for future writing. The + * actual write attempt will occur only upon flush, or when the internal buffer + * is full. + * + * @param socket + * The guac_socket being write to. + * + * @param buf + * The arbitrary buffer containing the data to be written. + * + * @param count + * The number of bytes contained within the buffer. + * + * @return + * The number of bytes written, or -1 if an error occurs. + */ +static ssize_t guac_socket_handle_write_handler(guac_socket* socket, + const void* buf, size_t count) { + + int retval; + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Acquire exclusive access to buffer */ + pthread_mutex_lock(&(data->buffer_lock)); + + /* Write provided data to buffer */ + retval = guac_socket_handle_write_buffered(socket, buf, count); + + /* Relinquish exclusive access to buffer */ + pthread_mutex_unlock(&(data->buffer_lock)); + + return retval; + +} + +/** + * Waits for data on the underlying file handle of the given socket to + * become available such that the next read operation will not block. + * + * @param socket + * The guac_socket to wait for. + * + * @param usec_timeout + * The maximum amount of time to wait for data, in microseconds, or -1 to + * potentially wait forever. + * + * @return + * A positive value on success, zero if the timeout elapsed and no data is + * available, or a negative value if an error occurs. + */ +static int guac_socket_handle_select_handler(guac_socket* socket, + int usec_timeout) { + + /* Wait for data on socket */ + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + int retval = guac_wait_for_handle(data->handle, usec_timeout); + + /* Properly set guac_error */ + if (retval > 0) { + guac_error = GUAC_STATUS_SEE_WINDOWS_ERROR; + guac_windows_error_code = retval; + guac_error_message = "Error while waiting for data on handle"; + return -1; + } + + else if (retval < 0) { + guac_error = GUAC_STATUS_TIMEOUT; + guac_error_message = "Timeout while waiting for data on handle"; + return 0; + } + + /* Data is ready */ + return 1; + +} + +/** + * Frees all implementation-specific data associated with the given socket, but + * not the socket object itself. + * + * @param socket + * The guac_socket whose associated data should be freed. + * + * @return + * Zero if the data was successfully freed, non-zero otherwise. This + * implementation always succeeds, and will always return zero. + */ +static int guac_socket_handle_free_handler(guac_socket* socket) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Destroy locks */ + pthread_mutex_destroy(&(data->socket_lock)); + pthread_mutex_destroy(&(data->buffer_lock)); + + /* Close file handles */ + CloseHandle(data->handle); + + free(data); + return 0; + +} + +/** + * Acquires exclusive access to the given socket. + * + * @param socket + * The guac_socket to which exclusive access is required. + */ +static void guac_socket_handle_lock_handler(guac_socket* socket) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Acquire exclusive access to socket */ + pthread_mutex_lock(&(data->socket_lock)); + +} + +/** + * Relinquishes exclusive access to the given socket. + * + * @param socket + * The guac_socket to which exclusive access is no longer required. + */ +static void guac_socket_handle_unlock_handler(guac_socket* socket) { + + guac_socket_handle_data* data = (guac_socket_handle_data*) socket->data; + + /* Relinquish exclusive access to socket */ + pthread_mutex_unlock(&(data->socket_lock)); + +} + +guac_socket* guac_socket_open_handle(HANDLE handle) { + + pthread_mutexattr_t lock_attributes; + + /* Allocate socket and associated data */ + guac_socket* socket = guac_socket_alloc(); + guac_socket_handle_data* data = malloc(sizeof(guac_socket_handle_data)); + + /* Store file handle as socket data */ + data->handle = handle; + data->written = 0; + socket->data = data; + + pthread_mutexattr_init(&lock_attributes); + pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED); + + /* Init locks */ + pthread_mutex_init(&(data->socket_lock), &lock_attributes); + pthread_mutex_init(&(data->buffer_lock), &lock_attributes); + + /* Set read/write handlers */ + socket->read_handler = guac_socket_handle_read_handler; + socket->write_handler = guac_socket_handle_write_handler; + socket->select_handler = guac_socket_handle_select_handler; + socket->lock_handler = guac_socket_handle_lock_handler; + socket->unlock_handler = guac_socket_handle_unlock_handler; + socket->flush_handler = guac_socket_handle_flush_handler; + socket->free_handler = guac_socket_handle_free_handler; + + return socket; + +} + diff --git a/src/libguac/tests/id/generate.c b/src/libguac/tests/id/generate.c index 3142a6f10..88c2f59e2 100644 --- a/src/libguac/tests/id/generate.c +++ b/src/libguac/tests/id/generate.c @@ -17,7 +17,7 @@ * under the License. */ -#include "id.h" +#include #include #include diff --git a/src/libguac/user-handshake.c b/src/libguac/user-handshake.c index 0863325f7..1b6895ca4 100644 --- a/src/libguac/user-handshake.c +++ b/src/libguac/user-handshake.c @@ -78,9 +78,11 @@ static void guac_user_log_guac_error(guac_user* user, guac_error_message); /* Otherwise just log with standard status string */ - else - guac_user_log(user, level, "%s: %s", message, - guac_status_string(guac_error)); + else { + char* status_string = guac_status_string(guac_error); + guac_user_log(user, level, "%s: %s", message, status_string); + free(status_string); + } } @@ -107,10 +109,12 @@ static void guac_user_log_handshake_failure(guac_user* user) { "Guacamole protocol violation. Perhaps the version of " "guacamole-client is incompatible with this version of " "libguac?"); - else + else { + char* status_string = guac_status_string(guac_error); guac_user_log(user, GUAC_LOG_WARNING, - "Guacamole handshake failed: %s", - guac_status_string(guac_error)); + "Guacamole handshake failed: %s", status_string); + free(status_string); + } } diff --git a/src/libguac/user.c b/src/libguac/user.c index d16f43b93..c2c7807b3 100644 --- a/src/libguac/user.c +++ b/src/libguac/user.c @@ -30,7 +30,7 @@ #include "guacamole/stream.h" #include "guacamole/timestamp.h" #include "guacamole/user.h" -#include "id.h" +#include #include "user-handlers.h" #include diff --git a/src/libguac/wait-handle.c b/src/libguac/wait-handle.c new file mode 100644 index 000000000..0e3133868 --- /dev/null +++ b/src/libguac/wait-handle.c @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +int guac_wait_for_handle(HANDLE handle, int usec_timeout) { + + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + OVERLAPPED overlapped = { 0 }; + + /* Set the event to be used to signal comm events */ + overlapped.hEvent = event; + + /* Request to wait for new data to be available */ + char buff[1]; + if (!ReadFile(handle, &buff, 0, NULL, &overlapped)) { + + DWORD error = GetLastError(); + + /* ERROR_IO_PENDING is expected in overlapped mode */ + if (error != ERROR_IO_PENDING) + return error; + + } + + int millis = (usec_timeout + 999) / 1000; + + DWORD result = WaitForSingleObject(event, millis); + + /* The wait attempt failed */ + if (result == WAIT_FAILED) + return GetLastError(); + + /* The event was signalled, which should indicate data is ready */ + else if (result == WAIT_OBJECT_0) + return 0; + + /* + * If the event didn't trigger and the wait didn't fail, data just isn't + * ready yet. + */ + return -1; + +} diff --git a/src/protocols/kubernetes/Makefile.am b/src/protocols/kubernetes/Makefile.am index e9316ce49..e488a83d9 100644 --- a/src/protocols/kubernetes/Makefile.am +++ b/src/protocols/kubernetes/Makefile.am @@ -69,5 +69,6 @@ libguac_client_kubernetes_la_LDFLAGS = \ -version-info 0:0:0 \ @PTHREAD_LIBS@ \ @SSL_LIBS@ \ - @WEBSOCKETS_LIBS@ + @WEBSOCKETS_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 1c393c30d..b8dd53479 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -144,7 +144,8 @@ libguac_client_rdp_la_LDFLAGS = \ -version-info 0:0:0 \ @CAIRO_LIBS@ \ @PTHREAD_LIBS@ \ - @RDP_LIBS@ + @RDP_LIBS@ \ + @CYWGIN_LDFLAGS@ libguac_client_rdp_la_LIBADD = \ @COMMON_LTLIB@ \ @@ -174,7 +175,8 @@ libguac_common_svc_client_la_CFLAGS = \ libguac_common_svc_client_la_LDFLAGS = \ -module -avoid-version -shared \ - @RDP_LIBS@ + @RDP_LIBS@ \ + @CYWGIN_LDFLAGS@ libguac_common_svc_client_la_LIBADD = \ @LIBGUAC_LTLIB@ @@ -199,7 +201,8 @@ libguacai_client_la_CFLAGS = \ libguacai_client_la_LDFLAGS = \ -module -avoid-version -shared \ @PTHREAD_LIBS@ \ - @RDP_LIBS@ + @RDP_LIBS@ \ + @CYWGIN_LDFLAGS@ libguacai_client_la_LIBADD = \ @COMMON_LTLIB@ \ @@ -264,4 +267,3 @@ EXTRA_DIST = \ $(rdp_keymaps) \ keymaps/generate.pl \ plugins/generate-entry-wrappers.pl - diff --git a/src/protocols/rdp/channels/rdpgfx.c b/src/protocols/rdp/channels/rdpgfx.c index 0fae972f6..a8b6da6cd 100644 --- a/src/protocols/rdp/channels/rdpgfx.c +++ b/src/protocols/rdp/channels/rdpgfx.c @@ -17,6 +17,8 @@ * under the License. */ +#include "config.h" + #include "channels/rdpgfx.h" #include "plugins/channels.h" #include "rdp.h" @@ -62,10 +64,19 @@ static void guac_rdp_rdpgfx_channel_connected(rdpContext* context, RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface; rdpGdi* gdi = context->gdi; +#ifdef CYGWIN_BUILD + + /* Return type is void under cygwin */ + gdi_graphics_pipeline_init(gdi, rdpgfx); + +#else + if (!gdi_graphics_pipeline_init(gdi, rdpgfx)) guac_client_log(client, GUAC_LOG_WARNING, "Rendering backend for RDPGFX " "channel could not be loaded. Graphics may not render at all!"); else + +#endif guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel will be used for " "the RDP Graphics Pipeline Extension."); diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am index 7bb1e3498..d586315fb 100644 --- a/src/protocols/ssh/Makefile.am +++ b/src/protocols/ssh/Makefile.am @@ -74,5 +74,5 @@ libguac_client_ssh_la_LDFLAGS = \ -version-info 0:0:0 \ @PTHREAD_LIBS@ \ @SSH_LIBS@ \ - @SSL_LIBS@ - + @SSL_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/protocols/telnet/Makefile.am b/src/protocols/telnet/Makefile.am index d0264f674..1b78b1393 100644 --- a/src/protocols/telnet/Makefile.am +++ b/src/protocols/telnet/Makefile.am @@ -61,5 +61,6 @@ libguac_client_telnet_la_LIBADD = \ libguac_client_telnet_la_LDFLAGS = \ -version-info 0:0:0 \ @PTHREAD_LIBS@ \ - @TELNET_LIBS@ + @TELNET_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index 64e8147e6..b19d123ab 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -64,7 +64,8 @@ libguac_client_vnc_la_CFLAGS = \ libguac_client_vnc_la_LDFLAGS = \ -version-info 0:0:0 \ @CAIRO_LIBS@ \ - @VNC_LIBS@ + @VNC_LIBS@ \ + @CYWGIN_LDFLAGS@ libguac_client_vnc_la_LIBADD = \ @COMMON_LTLIB@ \ diff --git a/src/pulse/Makefile.am b/src/pulse/Makefile.am index 3cb2d44cb..7ed891f10 100644 --- a/src/pulse/Makefile.am +++ b/src/pulse/Makefile.am @@ -42,5 +42,6 @@ libguac_pulse_la_LIBADD = \ @LIBGUAC_LTLIB@ libguac_pulse_la_LDFLAGS = \ - @PULSE_LIBS@ + @PULSE_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/terminal/Makefile.am b/src/terminal/Makefile.am index 38baedbad..89122b930 100644 --- a/src/terminal/Makefile.am +++ b/src/terminal/Makefile.am @@ -83,5 +83,6 @@ libguac_terminal_la_LDFLAGS = \ @MATH_LIBS@ \ @PANGO_LIBS@ \ @PANGOCAIRO_LIBS@ \ - @PTHREAD_LIBS@ + @PTHREAD_LIBS@ \ + @CYWGIN_LDFLAGS@ diff --git a/src/terminal/color-scheme.c b/src/terminal/color-scheme.c index c1a9e3ff7..60e68ad8c 100644 --- a/src/terminal/color-scheme.c +++ b/src/terminal/color-scheme.c @@ -17,6 +17,7 @@ * under the License. */ +#include "config.h" #include "terminal/color-scheme.h" #include "terminal/palette.h" @@ -71,6 +72,20 @@ static int guac_terminal_color_scheme_compare_token(const char* str_start, */ static void guac_terminal_color_scheme_strip_spaces(const char** str_start, const char** str_end) { + +#ifdef CYGWIN_BUILD + + /* Cast to unsigned below to avoid error: array subscript has type 'char' */ + + /* Strip leading spaces. */ + while (*str_start < *str_end && isspace((const unsigned char) (**str_start))) + (*str_start)++; + + /* Strip trailing spaces. */ + while (*str_end > *str_start && isspace((const unsigned char) *(*str_end - 1))) + (*str_end)--; + +#else /* Strip leading spaces. */ while (*str_start < *str_end && isspace(**str_start)) @@ -79,6 +94,9 @@ static void guac_terminal_color_scheme_strip_spaces(const char** str_start, /* Strip trailing spaces. */ while (*str_end > *str_start && isspace(*(*str_end - 1))) (*str_end)--; + +#endif + } /** diff --git a/src/terminal/named-colors.c b/src/terminal/named-colors.c index 948982afe..1ed23614e 100644 --- a/src/terminal/named-colors.c +++ b/src/terminal/named-colors.c @@ -17,6 +17,8 @@ * under the License. */ +#include "config.h" + #include "terminal/palette.h" #include @@ -758,7 +760,14 @@ static int guac_terminal_named_color_search(const void* a, const void* b) { for (; *key && *name; key++, name++) { /* Skip any spaces in key (name will never have spaces) */ + +#ifdef CYGWIN_BUILD + + /* Cast to unsigned to avoid error: array subscript has type 'char' */ + while (*key && isspace((unsigned char) *key)) key++; +#else while (*key && isspace(*key)) key++; +#endif /* Treat semi-colon as string terminator, to support parsing color names within a larger string (e.g. within the terminal color-scheme From 28821d7a29e4babcdc508f254c1ebc69e5916baf Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 2 Aug 2023 21:48:00 +0000 Subject: [PATCH 02/15] GUACAMOLE-1841: Always close event handles. --- src/guacd/connection.c | 8 +++++++- src/libguac/handle-helpers.c | 25 ++++++++++++++++++++----- src/libguac/wait-handle.c | 19 ++++++++++++++----- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/guacd/connection.c b/src/guacd/connection.c index f0ecf9772..1e8baacc1 100644 --- a/src/guacd/connection.c +++ b/src/guacd/connection.c @@ -373,8 +373,14 @@ static int guacd_add_user(guacd_proc* proc, guac_parser* parser, guac_socket* so return 1; } - /* Wait for the other end of the pipe to connect before attempting IO */ + /* Create an event to monitor for pipe connection */ HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (event == NULL) { + guacd_log(GUAC_LOG_ERROR, "Event creation failed."); + return 1; + } + + /* Wait for the other end of the pipe to connect before attempting IO */ OVERLAPPED overlapped = { 0 }; overlapped.hEvent = event; ConnectNamedPipe(pipe_handle, &overlapped); diff --git a/src/libguac/handle-helpers.c b/src/libguac/handle-helpers.c index 6c878336f..3c48dc795 100755 --- a/src/libguac/handle-helpers.c +++ b/src/libguac/handle-helpers.c @@ -39,6 +39,8 @@ int guac_read_from_handle( */ OVERLAPPED overlapped = { 0 }; overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (overlapped.hEvent == NULL) + return GetLastError(); /* Attempt to start the async read operation */ if (!ReadFile(handle, buffer, count, NULL, &overlapped)) { @@ -50,6 +52,7 @@ int guac_read_from_handle( * return it as the error code immediately. */ if (error != ERROR_IO_PENDING) { + CloseHandle(overlapped.hEvent); return error; } @@ -59,10 +62,14 @@ int guac_read_from_handle( * Wait on the result of the read. If any error occurs when waiting, * return the error. */ - if (!GetOverlappedResult(handle, &overlapped, num_bytes_read, TRUE)) - return GetLastError(); + if (!GetOverlappedResult(handle, &overlapped, num_bytes_read, TRUE)) { + DWORD error = GetLastError(); + CloseHandle(overlapped.hEvent); + return error; + } /* No errors occured, so the read was successful */ + CloseHandle(overlapped.hEvent); return 0; } @@ -75,6 +82,8 @@ int guac_write_to_handle( */ OVERLAPPED overlapped = { 0 }; overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (overlapped.hEvent == NULL) + return GetLastError(); /* Attempt to start the async write operation */ if (!WriteFile(handle, buffer, count, NULL, &overlapped)) { @@ -85,8 +94,10 @@ int guac_write_to_handle( * If an error other than the expected ERROR_IO_PENDING happens, * return it as the error code immediately. */ - if (error != ERROR_IO_PENDING) + if (error != ERROR_IO_PENDING) { + CloseHandle(overlapped.hEvent); return error; + } } @@ -94,10 +105,14 @@ int guac_write_to_handle( * Wait on the result of the write. If any error occurs when waiting, * return the error. */ - if (!GetOverlappedResult(handle, &overlapped, num_bytes_written, TRUE)) - return GetLastError(); + if (!GetOverlappedResult(handle, &overlapped, num_bytes_written, TRUE)) { + DWORD error = GetLastError(); + CloseHandle(overlapped.hEvent); + return error; + } /* No errors occured, so the write was successful */ + CloseHandle(overlapped.hEvent); return 0; } \ No newline at end of file diff --git a/src/libguac/wait-handle.c b/src/libguac/wait-handle.c index 0e3133868..bfba875b2 100644 --- a/src/libguac/wait-handle.c +++ b/src/libguac/wait-handle.c @@ -28,10 +28,12 @@ int guac_wait_for_handle(HANDLE handle, int usec_timeout) { + /* Create an event to be used to signal comm events */ HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (event == NULL) + return GetLastError(); + OVERLAPPED overlapped = { 0 }; - - /* Set the event to be used to signal comm events */ overlapped.hEvent = event; /* Request to wait for new data to be available */ @@ -41,8 +43,10 @@ int guac_wait_for_handle(HANDLE handle, int usec_timeout) { DWORD error = GetLastError(); /* ERROR_IO_PENDING is expected in overlapped mode */ - if (error != ERROR_IO_PENDING) + if (error != ERROR_IO_PENDING) { + CloseHandle(event); return error; + } } @@ -51,17 +55,22 @@ int guac_wait_for_handle(HANDLE handle, int usec_timeout) { DWORD result = WaitForSingleObject(event, millis); /* The wait attempt failed */ - if (result == WAIT_FAILED) + if (result == WAIT_FAILED) { + CloseHandle(event); return GetLastError(); + } /* The event was signalled, which should indicate data is ready */ - else if (result == WAIT_OBJECT_0) + else if (result == WAIT_OBJECT_0) { + CloseHandle(event); return 0; + } /* * If the event didn't trigger and the wait didn't fail, data just isn't * ready yet. */ + CloseHandle(event); return -1; } From ca84f4275c7fa757205f60d51ff95a22df02f846 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 10 Aug 2023 21:17:09 +0000 Subject: [PATCH 03/15] GUACAMOLE-1841: Ignore stack stackdump files. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e72000d8e..0bc7cd484 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ doc/*/doxygen-output # IDE metadata nbproject/ + +# Crash Reports +**/*.stackdump From f38ab48128d26cf91c8960d09e805cc3dd569f9a Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 28 Sep 2023 20:34:55 +0000 Subject: [PATCH 04/15] GUACAMOLE-1841: Provide an example of how to build under Windows. --- README-windows-build.md | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 README-windows-build.md diff --git a/README-windows-build.md b/README-windows-build.md new file mode 100644 index 000000000..fda9cc36e --- /dev/null +++ b/README-windows-build.md @@ -0,0 +1,84 @@ +# Building guacamole-server for Windows +The `guacamole-server` Windows build relies on compatibility features provided by Cygwin (most notably, a `fork()` implmentation), and therefore _must_ be built using Cygwin tools. Since no Cygwin cross-compilation environment exists, this means that `guacamole-server` can only be built for Windows using the Windows OS. This document describes a build that produces a working `guacd.exe`, as well as shared libraries for every supported protocol. + +## Build Specifics +In this example, `guacamole-server` was built under Cygwin, on a Windows Server 2022 x86_64 build node. Dependencies were installed using packages from Cygwin and MSYS2 (and built from source where no package is available, in the case of `libtelnet`). + +### Build Steps +1. Install Cygwin (version 2.926 used here) +2. Install MSYS2 (version 20230718 used here) +3. Install Cygwin packages: + * git + * make + * automake + * autoconf + * gcc-core + * libtool + * pkg-config + * libuuid-devel + * libwinpr2-devel + * libfreerdp2-devel +4. Install MSYS2 packages: + * autoconf-wrapper + * automake-wrapper + * diffutils + * make + * mingw-w64-x86_64-gcc + * mingw-w64-x86_64-cairo + * mingw-w64-x86_64-pango + * mingw-w64-x86_64-libwebsockets + * mingw-w64-x86_64-libvncserver + * mingw-w64-x86_64-libssh2 + * mingw-w64-x86_64-libtool + * mingw-w64-x86_64-libmariadbclient + * mingw-w64-x86_64-postgresql + * mingw-w64-x86_64-libpng + * mingw-w64-x86_64-libjpeg + * mingw-w64-x86_64-dlfcn + * mingw-w64-x86_64-pkg-config + * mingw-w64-x86_64-pulseaudio + * mingw-w64-x86_64-libvorbis + * mingw-w64-x86_64-libwebp + * mingw-w64-x86_64-zlib +5. Build `libtelnet` from source using MSYS2 bash shell + ``` + curl -s -L https://github.com/seanmiddleditch/libtelnet/releases/download/0.23/libtelnet-0.23.tar.gz | tar xz + cd libtelnet-0.23 + + autoreconf -fi + ./configure --disable-static --disable-util LDFLAGS="-Wl,-no-undefined" + + make LDFLAGS="-no-undefined" + make install + + # Required for the Cygwin build to understand how to link against this DLL + ln -s /usr/bin/msys-telnet-2.dll /usr/bin/libtelnet.dll + ``` +6. Build `guacamole-server` from source using Cygwin bash shell + ``` + # FIXME: Update this to check out master once this PR is ready for merge + git clone https://github.com/jmuehlner/guacamole-server.git + cd guacamole-server + git checkout GUACAMOLE-1841-cygwin-build + + autoreconf -fi + export LDFLAGS="-L/usr/bin/ -L/usr/lib/ -L/usr/local/lib -L/usr/local/bin -L/cygdrive/c/msys64/mingw64/bin -L/cygdrive/c/msys64/mingw64/lib -L/cygdrive/c/msys64/usr/bin" + export CFLAGS="-idirafter /cygdrive/c/msys64/mingw64/include/winpr2 -idirafter /usr/include/ -idirafter /cygdrive/c/msys64/mingw64/include -idirafter /cygdrive/c/msys64/mingw64/include/pango-1.0 -idirafter /cygdrive/c/msys64/mingw64/include/cairo -idirafter /cygdrive/c/msys64/mingw64/include/freerdp2 -idirafter /cygdrive/c/msys64/mingw64/include/glib-2.0 -idirafter /cygdrive/c/msys64/mingw64/include/harfbuzz -idirafter /cygdrive/c/msys64/mingw64/lib/glib-2.0/include -idirafter /cygdrive/c/msys64/usr/include" + export PKG_CONFIG_PATH="/cygdrive/c/msys64/mingw64/lib/pkgconfig/" + + # NOTE: The libfreerdp2-devel package provided by Cygwin is detected as a snapshot version, and must be explicitly allowed + ./configure --with-cygwin --enable-allow-freerdp-snapshots || cat config.log + + make + ``` + +## Closing Notes +The generated `guacd.exe` will run as expected when invoked from the Cygwin bash shell, but when run outside of a Cygwin environment (e.g. using powershell), connections using text-based protocols (ssh, telnet, kubernetes) will fail with `Fontconfig error: Cannot load default config file`. + +FIXME: Update this document to explain how to configure fonts under Windows once I figure it out. + +In addition, to enable running `guacd.exe` outside of a Cygwin environment, all (non-system) DLLs that it depends on should be copied into the same directory as the executable. These can be found by running `ldd` in the Cygwin bash shell - e.g. +``` +ldd /usr/local/sbin/guacd.exe +find /usr -name '*guac*.dll' | xargs ldd +``` From 2a0fe85192ba82970b2509db1f0fc4b371ec4998 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 3 Oct 2023 20:45:38 +0000 Subject: [PATCH 05/15] GUACAMOLE-1841: Update build instructions to build freerdp from source. --- README-windows-build.md | 71 +++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/README-windows-build.md b/README-windows-build.md index fda9cc36e..81a8eb3c8 100644 --- a/README-windows-build.md +++ b/README-windows-build.md @@ -2,22 +2,21 @@ The `guacamole-server` Windows build relies on compatibility features provided by Cygwin (most notably, a `fork()` implmentation), and therefore _must_ be built using Cygwin tools. Since no Cygwin cross-compilation environment exists, this means that `guacamole-server` can only be built for Windows using the Windows OS. This document describes a build that produces a working `guacd.exe`, as well as shared libraries for every supported protocol. ## Build Specifics -In this example, `guacamole-server` was built under Cygwin, on a Windows Server 2022 x86_64 build node. Dependencies were installed using packages from Cygwin and MSYS2 (and built from source where no package is available, in the case of `libtelnet`). +In this example, `guacamole-server` was built under Cygwin, on a Windows Server 2022 x86_64 build node. Dependencies were installed using packages from Cygwin and MSYS2 (and built from source where no suitable package is available, in the case of `libtelnet` and `libfreerdp2`). ### Build Steps 1. Install Cygwin (version 2.926 used here) 2. Install MSYS2 (version 20230718 used here) 3. Install Cygwin packages: - * git - * make - * automake * autoconf + * automake + * cmake + * git * gcc-core * libtool - * pkg-config * libuuid-devel - * libwinpr2-devel - * libfreerdp2-devel + * make + * pkg-config 4. Install MSYS2 packages: * autoconf-wrapper * automake-wrapper @@ -54,7 +53,54 @@ In this example, `guacamole-server` was built under Cygwin, on a Windows Server # Required for the Cygwin build to understand how to link against this DLL ln -s /usr/bin/msys-telnet-2.dll /usr/bin/libtelnet.dll ``` -6. Build `guacamole-server` from source using Cygwin bash shell +5. Build `libfreerdp2` from source using Cygwin bash shell + ``` + curl -s -L https://github.com/FreeRDP/FreeRDP/archive/2.10.0/FreeRDP-2.10.0.tar.gz | tar xz + cd FreeRDP-2.10.0 + + cmake \ + -DWITH_ALSA=OFF \ + -DWITH_CUPS=OFF \ + -DWITH_CHANNELS=ON \ + -DBUILTIN_CHANNELS=OFF \ + -DCHANNEL_URBDRC=OFF \ + -DWITH_CLIENT=ON \ + -DWITH_DIRECTFB=OFF \ + -DWITH_FFMPEG=OFF \ + -DWITH_GSM=OFF \ + -DWITH_GSSAPI=OFF \ + -DWITH_GSTREAMER_1_0=OFF \ + -DWITH_GSTREAMER_0_10=OFF \ + -DWITH_IPP=OFF \ + -DWITH_JPEG=ON \ + -DWITH_MANPAGES=ON \ + -DWITH_OPENH264=OFF \ + -DWITH_OPENSSL=ON \ + -DWITH_PCSC=OFF \ + -DWITH_PULSE=OFF \ + -DWITH_SERVER=OFF \ + -DWITH_SERVER_INTERFACE=OFF \ + -DWITH_SHADOW_X11=OFF \ + -DWITH_SHADOW_MAC=OFF \ + -DWITH_SSE2=$SSE2_SETTING \ + -DWITH_WAYLAND=OFF \ + -DWITH_X11=OFF \ + -DWITH_X264=OFF \ + -DWITH_XCURSOR=ON \ + -DWITH_XEXT=ON \ + -DWITH_XKBFILE=ON \ + -DWITH_XI=OFF \ + -DWITH_XINERAMA=OFF \ + -DWITH_XRENDER=OFF \ + -DWITH_XTEST=OFF \ + -DWITH_XV=OFF \ + -DWITH_ZLIB=ON \ + . + cmake . -G"Unix Makefiles" + make + make install + ``` +7. Build `guacamole-server` from source using Cygwin bash shell ``` # FIXME: Update this to check out master once this PR is ready for merge git clone https://github.com/jmuehlner/guacamole-server.git @@ -63,12 +109,9 @@ In this example, `guacamole-server` was built under Cygwin, on a Windows Server autoreconf -fi export LDFLAGS="-L/usr/bin/ -L/usr/lib/ -L/usr/local/lib -L/usr/local/bin -L/cygdrive/c/msys64/mingw64/bin -L/cygdrive/c/msys64/mingw64/lib -L/cygdrive/c/msys64/usr/bin" - export CFLAGS="-idirafter /cygdrive/c/msys64/mingw64/include/winpr2 -idirafter /usr/include/ -idirafter /cygdrive/c/msys64/mingw64/include -idirafter /cygdrive/c/msys64/mingw64/include/pango-1.0 -idirafter /cygdrive/c/msys64/mingw64/include/cairo -idirafter /cygdrive/c/msys64/mingw64/include/freerdp2 -idirafter /cygdrive/c/msys64/mingw64/include/glib-2.0 -idirafter /cygdrive/c/msys64/mingw64/include/harfbuzz -idirafter /cygdrive/c/msys64/mingw64/lib/glib-2.0/include -idirafter /cygdrive/c/msys64/usr/include" - export PKG_CONFIG_PATH="/cygdrive/c/msys64/mingw64/lib/pkgconfig/" - - # NOTE: The libfreerdp2-devel package provided by Cygwin is detected as a snapshot version, and must be explicitly allowed - ./configure --with-cygwin --enable-allow-freerdp-snapshots || cat config.log - + export CFLAGS="-idirafter /cygdrive/c/msys64/mingw64/include/winpr2 -idirafter /usr/include/ -idirafter /cygdrive/c/msys64/mingw64/include -idirafter /cygdrive/c/msys64/mingw64/include/pango-1.0 -idirafter /cygdrive/c/msys64/mingw64/include/cairo -idirafter /usr/local/include/freerdp2 -idirafter /cygdrive/c/msys64/mingw64/include/glib-2.0 -idirafter /cygdrive/c/msys64/mingw64/include/harfbuzz -idirafter /cygdrive/c/msys64/mingw64/lib/glib-2.0/include -idirafter /cygdrive/c/msys64/usr/include" + export PKG_CONFIG_PATH="/cygdrive/c/msys64/mingw64/lib/pkgconfig/:/usr/local/lib/pkgconfig" + ./configure --with-cygwin || cat config.log make ``` From b845e18c965aab1f0aae46fbff2776e3aa561d0e Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 11 Oct 2023 21:35:55 +0000 Subject: [PATCH 06/15] GUACAMOLE-1841: Log Windows PIDs instead of Cygwin PIDs. --- src/guacd/daemon.c | 36 ++++++++++++++++++++++++++++++++--- src/guacd/log.c | 13 ++++++++++++- src/guacd/proc.c | 27 ++++++++++++++++++++++++-- src/protocols/rdp/print-job.c | 16 +++++++++++++++- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/guacd/daemon.c b/src/guacd/daemon.c index 456e11505..b672fb088 100644 --- a/src/guacd/daemon.c +++ b/src/guacd/daemon.c @@ -46,6 +46,10 @@ #include #include +#ifdef CYGWIN_BUILD +#include +#endif + #define GUACD_DEV_NULL "/dev/null" #define GUACD_ROOT "/" @@ -106,9 +110,20 @@ static int daemonize() { return 1; } +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, pid); +#endif + /* Exit if we are the parent */ if (pid > 0) { - guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", pid); + + guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", +#ifdef CYGWIN_BUILD + windows_pid); +#else + pid); +#endif _exit(0); } @@ -124,7 +139,12 @@ static int daemonize() { /* Exit if we are the parent */ if (pid > 0) { - guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", pid); + guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", +#ifdef CYGWIN_BUILD + windows_pid); +#else + pid); +#endif _exit(0); } @@ -281,9 +301,19 @@ static void signal_stop_handler(int signal) { */ static void stop_process_callback(guacd_proc* proc, void* data) { +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, proc->pid); +#endif + guacd_log(GUAC_LOG_DEBUG, "Killing connection %s (%i)\n", - proc->client->connection_id, (int) proc->pid); + proc->client->connection_id, +#ifdef CYGWIN_BUILD + windows_pid); +#else + proc->pid); +#endif guacd_proc_stop(proc); diff --git a/src/guacd/log.c b/src/guacd/log.c index 9bda2adf0..c1943d1c1 100644 --- a/src/guacd/log.c +++ b/src/guacd/log.c @@ -29,6 +29,10 @@ #include #include +#ifdef CYGWIN_BUILD +#include +#endif + int guacd_log_level = GUAC_LOG_INFO; void vguacd_log(guac_client_log_level level, const char* format, @@ -90,8 +94,15 @@ void vguacd_log(guac_client_log_level level, const char* format, syslog(priority, "%s", message); /* Log to STDERR */ + pid_t pid = getpid(); + +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, pid); +#endif + fprintf(stderr, GUACD_LOG_NAME "[%i]: %s:\t%s\n", - getpid(), priority_name, message); + pid, priority_name, message); } diff --git a/src/guacd/proc.c b/src/guacd/proc.c index 7ab1db170..494df9fc7 100644 --- a/src/guacd/proc.c +++ b/src/guacd/proc.c @@ -48,6 +48,7 @@ #include "guacamole/socket-handle.h" #include #include +#include #endif /** @@ -465,8 +466,19 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) { /* Verify whether children were all properly reaped */ pid_t child_pid; while ((child_pid = waitpid(0, NULL, WNOHANG)) > 0) { + +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, child_pid); +#endif + guacd_log(GUAC_LOG_DEBUG, "Automatically reaped unreaped " - "(zombie) child process with PID %i.", child_pid); + "(zombie) child process with PID %i.", +#ifdef CYGWIN_BUILD + windows_pid); +#else + child_pid); +#endif } /* If running children remain, warn and forcibly kill */ @@ -575,8 +587,19 @@ 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) { + +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, child_pid); +#endif + guacd_log(GUAC_LOG_DEBUG, "Child process %i of connection \"%s\" has terminated", - child_pid, proc->client->connection_id); +#ifdef CYGWIN_BUILD + windows_pid, +#else + child_pid, +#endif + proc->client->connection_id); } guacd_log(GUAC_LOG_DEBUG, "All child processes for connection \"%s\" have been terminated.", diff --git a/src/protocols/rdp/print-job.c b/src/protocols/rdp/print-job.c index 49efbfdc4..a580f45b1 100644 --- a/src/protocols/rdp/print-job.c +++ b/src/protocols/rdp/print-job.c @@ -33,6 +33,10 @@ #include #include +#ifdef CYGWIN_BUILD +#include +#endif + /** * The command to run when filtering postscript to produce PDF. This must be * a NULL-terminated array of arguments, where the first argument is the name @@ -365,8 +369,18 @@ static pid_t guac_rdp_create_filter_process(guac_client* client, } /* Log fork success */ + +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, child_pid); +#endif guac_client_log(client, GUAC_LOG_INFO, "Created PDF filter process " - "PID=%i", child_pid); + "PID=%i", +#ifdef CYGWIN_BUILD + windows_pid); +#else + child_pid); +#endif /* Close unneeded ends of pipe */ close(stdin_pipe[0]); From 5ba11a934d332ca185f5f410604157ebf51cb73b Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 17 Oct 2023 23:05:30 +0000 Subject: [PATCH 07/15] GUACAMOLE-1841: Ensure that children get killed even under windows. --- src/guacd/proc.c | 52 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/guacd/proc.c b/src/guacd/proc.c index 494df9fc7..6e85e9411 100644 --- a/src/guacd/proc.c +++ b/src/guacd/proc.c @@ -43,6 +43,9 @@ #include #include +// Don't do it +#include + #ifdef CYGWIN_BUILD #include "move-pipe.h" #include "guacamole/socket-handle.h" @@ -275,16 +278,33 @@ static void* guacd_client_free_thread(void* data) { guacd_client_free* free_operation = (guacd_client_free*) data; + FILE* the_file = fopen("./guacd_client_free_thread.txt","a+"); + fprintf(the_file, "In guacd_client_free_thread\n"); + fclose(the_file); + /* Attempt to free client (this may never return if the client is * malfunctioning) */ guac_client_free(free_operation->client); + the_file = fopen("./guacd_client_free_thread.txt","a+"); + fprintf(the_file, "In guacd_client_free_thread, did guac_client_free()\n"); + fclose(the_file); + /* Signal that the client was successfully freed */ pthread_mutex_lock(&free_operation->completed_mutex); free_operation->completed = 1; + + the_file = fopen("./guacd_client_free_thread.txt","a+"); + fprintf(the_file, "In guacd_client_free_thread, set completed=1\n"); + fclose(the_file); + pthread_cond_broadcast(&free_operation->completed_cond); pthread_mutex_unlock(&free_operation->completed_mutex); + the_file = fopen("./guacd_client_free_thread.txt","a+"); + fprintf(the_file, "Done with guacd_client_free_thread\n"); + fclose(the_file); + return NULL; } @@ -317,11 +337,15 @@ static int guacd_timed_client_free(guac_client* client, int timeout) { .completed = 0 }; + fprintf(stderr, "In client_free_thread, waiting %i seconds\n", timeout); + /* Get current time */ struct timeval current_time; if (gettimeofday(¤t_time, NULL)) return 1; + fprintf(stderr, "In client_free_thread, got time of day\n"); + /* Calculate exact time that the free operation MUST complete by */ struct timespec deadline = { .tv_sec = current_time.tv_sec + timeout, @@ -333,9 +357,16 @@ static int guacd_timed_client_free(guac_client* client, int timeout) { if (pthread_mutex_lock(&free_operation.completed_mutex)) return 1; + fprintf(stderr, "In client_free_thread, got completed_mutex\n"); + /* Free the client in a separate thread, so we can time the free operation */ - if (!pthread_create(&client_free_thread, NULL, - guacd_client_free_thread, &free_operation)) { + int the_return = pthread_create(&client_free_thread, NULL, + guacd_client_free_thread, &free_operation); + fprintf(stderr, "pthread_create(): %i, errno: %i\n", the_return, errno); + + if (!the_return) { + + fprintf(stderr, "In client_free_thread, started guacd_client_free_thread...\n"); /* Wait a finite amount of time for the free operation to finish */ (void) pthread_cond_timedwait(&free_operation.completed_cond, @@ -363,6 +394,13 @@ guacd_proc* guacd_proc_self = NULL; */ static void signal_stop_handler(int signal) { +#ifdef CYGWIN_BUILD + /* Convert to a windows PID for logging */ + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, guacd_proc_self->pid); + fprintf(stderr, "I caught signal %i and am accordingly killing self with cyg %i, win %i\n", + signal, guacd_proc_self->pid, windows_pid); +#endif + /* Stop the current guacd proc */ guacd_proc_stop(guacd_proc_self); @@ -579,6 +617,14 @@ guacd_proc* guacd_create_proc(const char* protocol) { */ static void guacd_proc_kill(guacd_proc* proc) { + +#ifdef CYGWIN_BUILD + pid_t windows_pid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, proc->pid); + + guacd_log(GUAC_LOG_DEBUG, "Trying to kill child - cyg: %i, win: %i", proc->pid, windows_pid); +#endif + + /* Request orderly termination of process */ if (kill(proc->pid, SIGTERM)) guacd_log(GUAC_LOG_DEBUG, "Unable to request termination of " @@ -624,7 +670,7 @@ void guacd_proc_stop(guacd_proc* proc) { if (shutdown(proc->fd_socket, SHUT_RDWR) == -1) guacd_log(GUAC_LOG_ERROR, "Unable to shutdown internal socket for " "connection %s. Corresponding process may remain running but " - "inactive.", proc->client->connection_id); + "inactive. errno: %i", proc->client->connection_id, errno); /* Clean up our end of the socket */ close(proc->fd_socket); From 5ff7fe1bdba449d6eb9cc3e515b4bf7b3433f617 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 3 Nov 2023 21:36:44 +0000 Subject: [PATCH 08/15] More debugging stuff for why VNC gets stuck. --- src/common/surface.c | 59 +++++++++++++++++++++++++++++++++++---- src/guacd/proc.c | 5 ++-- src/libguac/client.c | 8 ++++++ src/libguac/encode-webp.c | 31 +++++++++++++++++++- src/protocols/vnc/vnc.c | 59 +++++++++++++++++++++++++++++++++++---- 5 files changed, 148 insertions(+), 14 deletions(-) diff --git a/src/common/surface.c b/src/common/surface.c index e5d1ca9f3..48335e016 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -33,6 +33,7 @@ #include #include #include +#include /** * The width of an update which should be considered negible and thus @@ -1805,24 +1806,34 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface, cairo_surface_t* rect; /* Use RGB24 if the image is fully opaque */ - if (opaque) + if (opaque) { + fprintf(stderr, "About to cairo_image_surface_create_for_data(CAIRO_FORMAT_RGB24)\n"); rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, surface->dirty_rect.width, surface->dirty_rect.height, surface->stride); + fprintf(stderr, "Did cairo_image_surface_create_for_data(CAIRO_FORMAT_RGB24)\n"); + } /* Otherwise ARGB32 is needed */ - else + else { + fprintf(stderr, "About to cairo_image_surface_create_for_data(CAIRO_FORMAT_ARGB32)\n"); rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32, surface->dirty_rect.width, surface->dirty_rect.height, surface->stride); + fprintf(stderr, "Did cairo_image_surface_create_for_data(CAIRO_FORMAT_ARGB32)\n"); + } /* Send WebP for rect */ + fprintf(stderr, "About to guac_client_stream_webp()\n"); guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, surface->dirty_rect.x, surface->dirty_rect.y, rect, guac_common_surface_suggest_quality(surface->client), surface->lossless ? 1 : 0); + fprintf(stderr, "Did guac_client_stream_webp()\n"); + fprintf(stderr, "About to cairo_surface_destroy()\n"); cairo_surface_destroy(rect); + fprintf(stderr, "Did cairo_surface_destroy()\n"); surface->realized = 1; /* Surface is no longer dirty */ @@ -1892,6 +1903,8 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { /* Flush final dirty rectangle to queue. */ __guac_common_surface_flush_to_queue(surface); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_flush_to_queue(surface)\n"); + guac_common_surface_bitmap_rect* current = surface->bitmap_queue; int i, j; int original_queue_length; @@ -1903,6 +1916,8 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { qsort(surface->bitmap_queue, surface->bitmap_queue_length, sizeof(guac_common_surface_bitmap_rect), __guac_common_surface_bitmap_rect_compare); + fprintf(stderr, "__guac_common_surface_flush did qsort()\n"); + /* Flush all rects in queue */ for (i=0; i < surface->bitmap_queue_length; i++) { @@ -1929,6 +1944,8 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { combined++; } + fprintf(stderr, "__guac_common_surface_flush did __guac_common_bound_rect()\n"); + } candidate++; @@ -1937,9 +1954,13 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { /* Re-add to queue if there's room and this update was modified or we expect others might be */ if ((combined > 1 || i < original_queue_length) - && surface->bitmap_queue_length < GUAC_COMMON_SURFACE_QUEUE_SIZE) + && surface->bitmap_queue_length < GUAC_COMMON_SURFACE_QUEUE_SIZE) { + __guac_common_surface_flush_to_queue(surface); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_flush_to_queue()\n"); + } + /* Flush as bitmap otherwise */ else if (surface->dirty) { @@ -1948,28 +1969,47 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { int opaque = __guac_common_surface_is_opaque(surface, &surface->dirty_rect); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_is_opaque()\n"); + /* Prefer WebP when reasonable */ if (__guac_common_surface_should_use_webp(surface, - &surface->dirty_rect)) + &surface->dirty_rect)) { + + fprintf(stderr, "__guac_common_surface_flush about to __guac_common_surface_flush_to_webp()\n"); __guac_common_surface_flush_to_webp(surface, opaque); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_flush_to_webp()\n"); + } + /* If not WebP, JPEG is the next best (lossy) choice */ else if (opaque && __guac_common_surface_should_use_jpeg( - surface, &surface->dirty_rect)) + surface, &surface->dirty_rect)) { + fprintf(stderr, "__guac_common_surface_flush about to __guac_common_surface_flush_to_jpeg()\n"); __guac_common_surface_flush_to_jpeg(surface); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_flush_to_jpeg()\n"); + } + /* Use PNG if no lossy formats are appropriate */ - else + else { + fprintf(stderr, "__guac_common_surface_flush about to __guac_common_surface_flush_to_png()\n"); __guac_common_surface_flush_to_png(surface, opaque); + fprintf(stderr, "__guac_common_surface_flush did __guac_common_surface_flush_to_png()\n"); + } + } } current++; + fprintf(stderr, "__guac_common_surface_flush did current++ - %p\n", (void*) current); + } + fprintf(stderr, "__guac_common_surface_flush Flush complete\n"); + /* Flush complete */ surface->bitmap_queue_length = 0; @@ -1977,16 +2017,23 @@ static void __guac_common_surface_flush(guac_common_surface* surface) { void guac_common_surface_flush(guac_common_surface* surface) { + fprintf(stderr, "guac_common_surface_flush about to pthread_mutex_lock(&surface->_lock)\n"); pthread_mutex_lock(&surface->_lock); + fprintf(stderr, "guac_common_surface_flush got pthread_mutex_lock(&surface->_lock)\n"); /* Flush any applicable layer properties */ __guac_common_surface_flush_properties(surface); + fprintf(stderr, "guac_common_surface_flush did __guac_common_surface_flush_properties(surface)\n"); /* Flush surface contents */ __guac_common_surface_flush(surface); + fprintf(stderr, "guac_common_surface_flush did __guac_common_surface_flush(surface)\n"); pthread_mutex_unlock(&surface->_lock); + fprintf(stderr, "guac_common_surface_flush did pthread_mutex_unlock(&surface->_lock)\n"); + + } void guac_common_surface_dup(guac_common_surface* surface, diff --git a/src/guacd/proc.c b/src/guacd/proc.c index 6e85e9411..f9babe96b 100644 --- a/src/guacd/proc.c +++ b/src/guacd/proc.c @@ -167,7 +167,8 @@ static void guacd_proc_add_user(guacd_proc* proc, HANDLE handle, int owner) { /* Start user thread */ pthread_t user_thread; - pthread_create(&user_thread, NULL, guacd_user_thread, params); + int the_return = pthread_create(&user_thread, NULL, guacd_user_thread, params); + fprintf(stderr, "guacd_proc_add_user pthread_create(): %i, errno: %i\n", the_return, errno); pthread_detach(user_thread); } @@ -362,7 +363,7 @@ static int guacd_timed_client_free(guac_client* client, int timeout) { /* Free the client in a separate thread, so we can time the free operation */ int the_return = pthread_create(&client_free_thread, NULL, guacd_client_free_thread, &free_operation); - fprintf(stderr, "pthread_create(): %i, errno: %i\n", the_return, errno); + fprintf(stderr, "guacd_timed_client_free pthread_create(): %i, errno: %i\n", the_return, errno); if (!the_return) { diff --git a/src/libguac/client.c b/src/libguac/client.c index b904fb7e4..ddaca1045 100644 --- a/src/libguac/client.c +++ b/src/libguac/client.c @@ -913,20 +913,28 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket, cairo_surface_t* surface, int quality, int lossless) { #ifdef ENABLE_WEBP + + fprintf(stderr, "guac_client_stream_webp about to guac_client_alloc_stream()\n"); /* Allocate new stream for image */ guac_stream* stream = guac_client_alloc_stream(client); + fprintf(stderr, "guac_client_stream_webp about did guac_client_alloc_stream()\n"); /* Declare stream as containing image data */ guac_protocol_send_img(socket, stream, mode, layer, "image/webp", x, y); + fprintf(stderr, "guac_client_stream_webp about did guac_protocol_send_img()\n"); /* Write WebP data */ guac_webp_write(socket, stream, surface, quality, lossless); + fprintf(stderr, "guac_client_stream_webp about did guac_webp_write()\n"); /* Terminate stream */ guac_protocol_send_end(socket, stream); + fprintf(stderr, "guac_client_stream_webp about did guac_protocol_send_end()\n"); + /* Free allocated stream */ guac_client_free_stream(client, stream); + fprintf(stderr, "guac_client_stream_webp about did guac_client_free_stream()\n"); #else /* Do nothing if WebP support is not built in */ #endif diff --git a/src/libguac/encode-webp.c b/src/libguac/encode-webp.c index 43c5a00c5..f77050fe2 100644 --- a/src/libguac/encode-webp.c +++ b/src/libguac/encode-webp.c @@ -171,11 +171,19 @@ int guac_webp_write(guac_socket* socket, guac_stream* stream, int x, y; + fprintf(stderr, "guac_webp_write about to cairo_image_surface_get_width()\n"); + int width = cairo_image_surface_get_width(surface); + + fprintf(stderr, "guac_webp_write did cairo_image_surface_get_width()\n"); int height = cairo_image_surface_get_height(surface); + fprintf(stderr, "guac_webp_write did cairo_image_surface_get_height()\n"); int stride = cairo_image_surface_get_stride(surface); + fprintf(stderr, "guac_webp_write did cairo_image_surface_get_stride()\n"); cairo_format_t format = cairo_image_surface_get_format(surface); + fprintf(stderr, "guac_webp_write did cairo_image_surface_get_format()\n"); unsigned char* data = cairo_image_surface_get_data(surface); + fprintf(stderr, "guac_webp_write did cairo_image_surface_get_data()\n"); if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) { guac_error = GUAC_STATUS_INTERNAL_ERROR; @@ -185,11 +193,18 @@ int guac_webp_write(guac_socket* socket, guac_stream* stream, /* Flush pending operations to surface */ cairo_surface_flush(surface); + fprintf(stderr, "guac_webp_write did cairo_surface_flush()\n"); /* Configure WebP compression bits */ WebPConfig config; - if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality)) + if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality)) { + + fprintf(stderr, "not WebPConfigPreset()\n"); + return -1; + } + + fprintf(stderr, "guac_webp_write did WebPConfigPreset()\n"); /* Add additional tuning */ config.lossless = lossless; @@ -200,18 +215,26 @@ int guac_webp_write(guac_socket* socket, guac_stream* stream, /* Validate configuration */ WebPValidateConfig(&config); + fprintf(stderr, "guac_webp_write did WebPValidateConfig()\n"); + /* Set up WebP picture */ WebPPictureInit(&picture); picture.use_argb = 1; picture.width = width; picture.height = height; + fprintf(stderr, "guac_webp_write did WebPPictureInit()\n"); + /* Allocate and init writer */ WebPPictureAlloc(&picture); + + fprintf(stderr, "guac_webp_write did WebPPictureAlloc()\n"); picture.writer = guac_webp_stream_write; picture.custom_ptr = &writer; guac_webp_stream_writer_init(&writer, socket, stream); + fprintf(stderr, "guac_webp_write did guac_webp_stream_writer_init()\n"); + /* Copy image data into WebP picture */ argb_output = picture.argb; for (y = 0; y < height; y++) { @@ -246,12 +269,18 @@ int guac_webp_write(guac_socket* socket, guac_stream* stream, /* Encode image */ WebPEncode(&config, &picture); + fprintf(stderr, "guac_webp_write did WebPEncode()\n"); + /* Free picture */ WebPPictureFree(&picture); + fprintf(stderr, "guac_webp_write did WebPPictureFree()\n"); + /* Ensure all data is written */ guac_webp_flush_data(&writer); + fprintf(stderr, "guac_webp_write did guac_webp_flush_data()\n"); + return 0; } diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index 3b94d5e75..3ca325b95 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -262,7 +262,10 @@ static int guac_vnc_wait_for_messages(rfbClient* rfb_client, int timeout) { return 1; /* If no data on buffer, wait for data on socket */ - return WaitForMessage(rfb_client, timeout); + fprintf(stderr, "About to WaitForMessage(%p, %i)\n", (void*) rfb_client, timeout); + int the_value = WaitForMessage(rfb_client, timeout); + fprintf(stderr, "Succesfully got WaitForMessage() value: %i\n", the_value); + return the_value; } @@ -419,6 +422,8 @@ void* guac_vnc_client_thread(void* data) { /* Set remaining client data */ vnc_client->rfb_client = rfb_client; + fprintf(stderr, "Did vnc_client->rfb_client = rfb_client\n"); + /* Set up screen recording, if requested */ if (settings->recording_path != NULL) { vnc_client->recording = guac_recording_create(client, @@ -431,14 +436,20 @@ void* guac_vnc_client_thread(void* data) { settings->recording_include_keys); } + fprintf(stderr, "Did guac_recording_create\n"); + /* Create display */ vnc_client->display = guac_common_display_alloc(client, rfb_client->width, rfb_client->height); + fprintf(stderr, "Did guac_common_display_alloc\n"); + /* Use lossless compression only if requested (otherwise, use default * heuristics) */ guac_common_display_set_lossless(vnc_client->display, settings->lossless); + fprintf(stderr, "Did guac_common_display_set_lossless\n"); + /* If not read-only, set an appropriate cursor */ if (settings->read_only == 0) { if (settings->remote_cursor) @@ -448,21 +459,35 @@ void* guac_vnc_client_thread(void* data) { } + fprintf(stderr, "Did guac_common_cursor_set_\n"); + guac_socket_flush(client->socket); + fprintf(stderr, "Did guac_socket_flush\n"); + guac_timestamp last_frame_end = guac_timestamp_current(); + fprintf(stderr, "last_frame_end: %lu\n", last_frame_end); + fprintf(stderr, "client->state: %i\n", client->state); + /* Handle messages from VNC server while client is running */ while (client->state == GUAC_CLIENT_RUNNING) { /* Wait for start of frame */ int wait_result = guac_vnc_wait_for_messages(rfb_client, GUAC_VNC_FRAME_START_TIMEOUT); + + fprintf(stderr, "guac_vnc_wait_for_messages result: %i\n", wait_result); + if (wait_result > 0) { int processing_lag = guac_client_get_processing_lag(client); + + fprintf(stderr, "processing_lag: %i\n", processing_lag); guac_timestamp frame_start = guac_timestamp_current(); + fprintf(stderr, "frame_start: %lu\n", frame_start); + /* Read server messages until frame is built */ do { @@ -482,21 +507,34 @@ void* guac_vnc_client_thread(void* data) { frame_remaining = frame_start + GUAC_VNC_FRAME_DURATION - frame_end; + fprintf(stderr, "frame_end: %lu\n", frame_end); + /* Calculate time that client needs to catch up */ int time_elapsed = frame_end - last_frame_end; int required_wait = processing_lag - time_elapsed; + fprintf(stderr, "required_wait: %i\n", required_wait); + /* Increase the duration of this frame if client is lagging */ - if (required_wait > GUAC_VNC_FRAME_TIMEOUT) + if (required_wait > GUAC_VNC_FRAME_TIMEOUT) { + + fprintf(stderr, "waiting for: %i\n", required_wait); wait_result = guac_vnc_wait_for_messages(rfb_client, required_wait*1000); + fprintf(stderr, "wait_result: %i\n", wait_result); + } /* Wait again if frame remaining */ - else if (frame_remaining > 0) + else if (frame_remaining > 0) { + fprintf(stderr, "frame_remaining: %i, waiting for: %i\n", frame_remaining, GUAC_VNC_FRAME_TIMEOUT); wait_result = guac_vnc_wait_for_messages(rfb_client, GUAC_VNC_FRAME_TIMEOUT*1000); - else + fprintf(stderr, "wait_result: %i\n", wait_result); + } + else { + fprintf(stderr, "breaking\n"); break; + } } while (wait_result > 0); @@ -505,21 +543,32 @@ void* guac_vnc_client_thread(void* data) { * two subsequent frames, and that this time should thus be * excluded from the required wait period of the next frame). */ last_frame_end = frame_start; + fprintf(stderr, "final last_frame_end: %lu\n", last_frame_end); } + fprintf(stderr, "final wait_result: %i\n", wait_result); + /* If an error occurs, log it and fail */ - if (wait_result < 0) + if (wait_result < 0) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Connection closed."); + } /* Flush frame */ + fprintf(stderr, "about to guac_common_surface_flush()\n"); guac_common_surface_flush(vnc_client->display->default_surface); + fprintf(stderr, "did guac_common_surface_flush()\n"); + guac_client_end_frame(client); + fprintf(stderr, "did guac_client_end_frame()\n"); + guac_socket_flush(client->socket); + fprintf(stderr, "did guac_socket_flush()\n"); } /* Kill client and finish connection */ + fprintf(stderr, "about to guac_client_stop()\n"); guac_client_stop(client); guac_client_log(client, GUAC_LOG_INFO, "Internal VNC client disconnected"); return NULL; From 9ab06d5cc3c9b54be36a9e8d64cac131b0766f8a Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 10 Nov 2023 00:48:10 +0000 Subject: [PATCH 09/15] What happens if we just set the timout to 0 for WaitForMessage()? --- src/protocols/vnc/vnc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index 3ca325b95..06cc2b23a 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -261,6 +261,9 @@ static int guac_vnc_wait_for_messages(rfbClient* rfb_client, int timeout) { if (rfb_client->buffered) return 1; + // LOLNO + timeout = 0; + /* If no data on buffer, wait for data on socket */ fprintf(stderr, "About to WaitForMessage(%p, %i)\n", (void*) rfb_client, timeout); int the_value = WaitForMessage(rfb_client, timeout); From 3a8a23e0e1f55e9dd21a2f0dd5f6b1243c9cb486 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 10 Nov 2023 18:11:52 +0000 Subject: [PATCH 10/15] yet more logging. --- src/protocols/vnc/vnc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index 06cc2b23a..8bb56c01a 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -498,12 +498,15 @@ void* guac_vnc_client_thread(void* data) { int frame_remaining; /* Handle any message received */ + fprintf(stderr, "About to HandleRFBServerMessage()\n"); if (!HandleRFBServerMessage(rfb_client)) { + fprintf(stderr, "Failed to HandleRFBServerMessage()\n"); guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error handling message from VNC server."); break; } + fprintf(stderr, "Succeeded at HandleRFBServerMessage()\n"); /* Calculate time remaining in frame */ frame_end = guac_timestamp_current(); From 61c451abc8dc8d9a3b94becb9b64de589cab8cb0 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 15 Nov 2023 19:01:09 +0000 Subject: [PATCH 11/15] Maybe we can just use built in UUID functions for generating IDs? --- configure.ac | 83 ++++++++++++++++++---------------- src/libguac/client.c | 43 +++++++++++++++++- src/libguac/guacamole/client.h | 8 ++++ src/libguac/id.c | 51 +++++++++++++++------ 4 files changed, 129 insertions(+), 56 deletions(-) diff --git a/configure.ac b/configure.ac index 6d3a56398..5639da83e 100644 --- a/configure.ac +++ b/configure.ac @@ -94,49 +94,52 @@ AC_CHECK_LIB([dl], [dlopen], AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"), [#include ])]) -# -# libuuid -# - -have_libuuid=disabled -AC_ARG_WITH([libuuid], - [AS_HELP_STRING([--with-libuuid], - [use libuuid to generate unique identifiers @<:@default=check@:>@])], - [], - [with_libuuid=check]) - -if test "x$with_libuuid" != "xno" +if test "x$with_cygwin" = "xno" then - have_libuuid=yes - AC_CHECK_LIB([uuid], [uuid_generate], - [UUID_LIBS=-luuid] - [AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])], - [have_libuuid=no]) -fi + # + # libuuid + # + + have_libuuid=disabled + AC_ARG_WITH([libuuid], + [AS_HELP_STRING([--with-libuuid], + [use libuuid to generate unique identifiers @<:@default=check@:>@])], + [], + [with_libuuid=check]) -# OSSP UUID (if libuuid is unavilable) -if test "x${have_libuuid}" != "xyes" -then + if test "x$with_libuuid" != "xno" + then + have_libuuid=yes + AC_CHECK_LIB([uuid], [uuid_generate], + [UUID_LIBS=-luuid] + [AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])], + [have_libuuid=no]) + fi - AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid], - AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid], - AC_MSG_ERROR([ - -------------------------------------------- - Unable to find libuuid or the OSSP UUID library. - Either libuuid (from util-linux) or the OSSP UUID library is required for - guacamole-server to be built. - --------------------------------------------]))) - - # Check for and validate OSSP uuid.h header - AC_CHECK_HEADERS([ossp/uuid.h]) - AC_CHECK_DECL([uuid_make],, - AC_MSG_ERROR("No OSSP uuid.h found in include path"), - [#ifdef HAVE_OSSP_UUID_H - #include - #else - #include - #endif - ]) + # OSSP UUID (if libuuid is unavilable) + if test "x${have_libuuid}" != "xyes" + then + + AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid], + AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid], + AC_MSG_ERROR([ + -------------------------------------------- + Unable to find libuuid or the OSSP UUID library. + Either libuuid (from util-linux) or the OSSP UUID library is required for + guacamole-server to be built. + --------------------------------------------]))) + + # Check for and validate OSSP uuid.h header + AC_CHECK_HEADERS([ossp/uuid.h]) + AC_CHECK_DECL([uuid_make],, + AC_MSG_ERROR("No OSSP uuid.h found in include path"), + [#ifdef HAVE_OSSP_UUID_H + #include + #else + #include + #endif + ]) + fi fi # cunit diff --git a/src/libguac/client.c b/src/libguac/client.c index ddaca1045..6c3b3aedd 100644 --- a/src/libguac/client.c +++ b/src/libguac/client.c @@ -46,6 +46,10 @@ #include #include +#ifdef CYGWIN_BUILD +#include +#endif + /** * The number of nanoseconds between times that the pending users list will be * synchronized and emptied (250 milliseconds aka 1/4 second). @@ -161,10 +165,24 @@ void guac_client_free_stream(guac_client* client, guac_stream* stream) { * * @param data * The client for which all pending users should be promoted. + * + * @param timerOrWaitFired + * Unused - Windows only. */ -static void guac_client_promote_pending_users(union sigval data) { +static void guac_client_promote_pending_users( +#ifdef CYGWIN_BUILD + LPVOID data, + BOOLEAN timerOrWaitFired +#else + union sigval data +#endif + ) { +#ifdef CYGWIN_BUILD + guac_client* client = (guac_client*) data; +#else guac_client* client = (guac_client*) data.sival_ptr; +#endif pthread_mutex_lock(&(client->__pending_users_timer_mutex)); @@ -365,8 +383,13 @@ void guac_client_free(guac_client* client) { pthread_mutex_unlock(&(client->__pending_users_timer_mutex)); /* If the timer was registered, stop it before destroying the lock */ - if (was_started) + if (was_started) { +#ifdef CYGWIN_BUILD + DeleteTimerQueueTimer(NULL, client->__pending_users_timer, NULL); +#else timer_delete(client->__pending_users_timer); +#endif + } pthread_mutex_destroy(&(client->__pending_users_timer_mutex)); @@ -493,6 +516,21 @@ static int guac_client_start_pending_users_timer(guac_client* client) { return 0; } +#ifdef CYGWIN_BUILD + if (!CreateTimerQueueTimer( + &(client->__pending_users_timer)), + NULL, + guac_client_promote_pending_users, + client, + GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL, + GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL + 0) { + + // oh noes error + return 1; + } +#else + /* Configure the timer to synchronize and clear the pending users */ struct sigevent signal_config = { .sigev_notify = SIGEV_THREAD, @@ -507,6 +545,7 @@ static int guac_client_start_pending_users_timer(guac_client* client) { pthread_mutex_unlock(&(client->__pending_users_timer_mutex)); return 1; } +#endif /* Configure the pending users timer to run on the defined interval */ struct itimerspec time_config = { diff --git a/src/libguac/guacamole/client.h b/src/libguac/guacamole/client.h index 328965667..d904ce4cc 100644 --- a/src/libguac/guacamole/client.h +++ b/src/libguac/guacamole/client.h @@ -45,6 +45,10 @@ #include #include +#ifdef CYGWIN_BUILD +#include +#endif + struct guac_client { /** @@ -192,7 +196,11 @@ struct guac_client { * use within the client. This will be NULL until the first user joins * the connection, as it is lazily instantiated at that time. */ +#ifdef CYGWIN_BUILD + HANDLE __pending_users_timer; +#else timer_t __pending_users_timer; +#endif /** * A flag storing the current state of the pending users timer. diff --git a/src/libguac/id.c b/src/libguac/id.c index 610f836d5..c42bdff81 100644 --- a/src/libguac/id.c +++ b/src/libguac/id.c @@ -22,7 +22,10 @@ #include "guacamole/error.h" #include -#if defined(HAVE_LIBUUID) + +#ifdef CYGWIN_BUILD +#include +#elif defined(HAVE_LIBUUID) #include #elif defined(HAVE_OSSP_UUID_H) #include @@ -37,6 +40,38 @@ char* guac_generate_id(char prefix) { char* buffer; char* identifier; + /* Allocate buffer for future formatted ID */ + buffer = malloc(GUAC_UUID_LEN + 1); + if (buffer == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for unique ID"; + return NULL; + } + + identifier = &(buffer[1]); + +#ifdef CYGWIN_BUILD + + /* Generate a UUID using a built in windows function */ + UUID uuid; + UuidCreate(&uuid); + + /* Convert the UUID to an all-caps, null-terminated tring */ + RPC_CSTR uuid_string; + if (UuidToString(uuid, &uuid_string) == RPC_S_OUT_OF_MEMORY) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for unique ID"; + return NULL; + } + + /* Copy over lowercase letters to the final target string */ + for (int i = 0; i < GUAC_UUID_LEN; i++) + identifier[i] = tolower(uuid_string[i]); + + RpcStringFree(uuid_string); + +#else + /* Prepare object to receive generated UUID */ #ifdef HAVE_LIBUUID uuid_t uuid; @@ -61,19 +96,6 @@ char* guac_generate_id(char prefix) { } #endif - /* Allocate buffer for future formatted ID */ - buffer = malloc(GUAC_UUID_LEN + 1); - if (buffer == NULL) { -#ifndef HAVE_LIBUUID - uuid_destroy(uuid); -#endif - guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for unique ID"; - return NULL; - } - - identifier = &(buffer[1]); - /* Convert UUID to string to produce unique identifier */ #ifdef HAVE_LIBUUID uuid_unparse_lower(uuid, identifier); @@ -89,6 +111,7 @@ char* guac_generate_id(char prefix) { /* Clean up generated UUID */ uuid_destroy(uuid); +#endif #endif buffer[0] = prefix; From 269031b6afacaea523876148dc330a116ea92e2c Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 15 Nov 2023 23:22:16 +0000 Subject: [PATCH 12/15] Dockerfile to test cross-compilation build. --- Dockerfile.windows | 30 ++++++++++++++++++++++++++++++ scripts/build-cairo.sh | 18 ++++++++++++++++++ scripts/build-guac.sh | 16 ++++++++++++++++ scripts/install-msys2.sh | 24 ++++++++++++++++++++++++ src/libguac/guacamole/client.h | 2 +- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.windows create mode 100644 scripts/build-cairo.sh create mode 100644 scripts/build-guac.sh create mode 100644 scripts/install-msys2.sh diff --git a/Dockerfile.windows b/Dockerfile.windows new file mode 100644 index 000000000..912f148b1 --- /dev/null +++ b/Dockerfile.windows @@ -0,0 +1,30 @@ +FROM ubuntu:22.04 + +RUN apt-get -y update +RUN apt-get -y install software-properties-common + +RUN apt-get -y install \ + autoconf \ + autotools-dev \ + cmake \ + git \ + libtool \ + make \ + meson \ + pkg-config \ + wget \ + zstd \ + gpg \ + wine \ + gcc-mingw-w64-x86-64-win32 \ + gcc-mingw-w64-x86-64-win32-runtime + +COPY scripts/install-msys2.sh /install-msys2.sh +RUN bash /install-msys2.sh + +RUN mkdir /guacamole-server +COPY . /guacamole-server + +COPY /scripts/build-guac.sh /build-guac.sh +RUN bash /build-guac.sh + diff --git a/scripts/build-cairo.sh b/scripts/build-cairo.sh new file mode 100644 index 000000000..fe92f8370 --- /dev/null +++ b/scripts/build-cairo.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +wget -qO- https://www.cairographics.org/releases/pixman-0.42.2.tar.gz | tar xz +pushd pixman-0.42.2 + +./configure --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32 \ + CPPFLAGS="-I/usr/x86_64-w64-mingw32/include" LDFLAGS="-L/usr/x86_64-w64-mingw32/lib/" +make +make install +popd + +wget -qO- https://www.cairographics.org/releases/cairo-1.18.0.tar.xz | tar xJ +pushd cairo-1.18.0 + +meson setup --cross-file /x86_64-w64-mingw32.txt build-mingw +meson compile -C build-mingw +meson install diff --git a/scripts/build-guac.sh b/scripts/build-guac.sh new file mode 100644 index 000000000..65175660d --- /dev/null +++ b/scripts/build-guac.sh @@ -0,0 +1,16 @@ +#!/bin/bash +#set -e + +cd guacamole-server + +export PKG_CONFIG_PATH="/quasi-msys2/root/ucrt64/lib/pkgconfig/" +export LDFLAGS="-L/quasi-msys2/root/ucrt64/bin/" +export CFLAGS="-I/quasi-msys2/root/ucrt64/include/ -I/usr/x86_64-w64-mingw32/include" + +autoreconf -fi +./configure --host=x86_64-w64-mingw32 --with-cygwin --with-telnet=no --with-ssh=no --with-rdp=no --with-vnc=no --disable-kubernetes --disable-guacenc --disable-guacd --disable-guaclog + +cat config.log + +make +make install diff --git a/scripts/install-msys2.sh b/scripts/install-msys2.sh new file mode 100644 index 000000000..273879707 --- /dev/null +++ b/scripts/install-msys2.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e +set -x + +#bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + +git clone https://github.com/holyblackcat/quasi-msys2 +pushd quasi-msys2 + +make install \ + mingw-w64-ucrt-x86_64-cairo \ + mingw-w64-ucrt-x86_64-gcc \ + mingw-w64-ucrt-x86_64-gdb \ + mingw-w64-ucrt-x86_64-libpng \ + mingw-w64-ucrt-x86_64-libjpeg-turbo \ + mingw-w64-ucrt-x86_64-freerdp \ + mingw-w64-ucrt-x86_64-libvncserver \ + mingw-w64-ucrt-x86_64-dlfcn \ + mingw-w64-ucrt-x86_64-libgcrypt + +ln -s /quasi-msys2/root/ucrt64/bin/libcairo-2.dll /quasi-msys2/root/ucrt64/bin/libcairo.dll +ln -s /quasi-msys2/root/ucrt64/bin/libjpeg-8.dll /quasi-msys2/root/ucrt64/bin/libjpeg.dll +ln -s /quasi-msys2/root/ucrt64/bin/libpng16-16.dll /quasi-msys2/root/ucrt64/bin/libpng.dll diff --git a/src/libguac/guacamole/client.h b/src/libguac/guacamole/client.h index d904ce4cc..9da16e2ab 100644 --- a/src/libguac/guacamole/client.h +++ b/src/libguac/guacamole/client.h @@ -46,7 +46,7 @@ #include #ifdef CYGWIN_BUILD -#include +#include #endif struct guac_client { From c36794b4f7684be9d16e59d666eee439562c0d7b Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 16 Nov 2023 00:31:17 +0000 Subject: [PATCH 13/15] Disable the warning that's breaking the build. --- README-unit-testing.md | 2 +- configure.ac | 4 ++-- scripts/build-guac.sh | 4 ++++ scripts/install-msys2.sh | 21 +++++++++++++++++---- src/common-ssh/Makefile.am | 2 +- src/common-ssh/tests/Makefile.am | 2 +- src/common/Makefile.am | 2 +- src/common/tests/Makefile.am | 2 +- src/guacd/Makefile.am | 2 +- src/guacenc/Makefile.am | 2 +- src/guaclog/Makefile.am | 2 +- src/libguac/Makefile.am | 2 +- src/libguac/tests/Makefile.am | 2 +- src/protocols/kubernetes/Makefile.am | 2 +- src/protocols/kubernetes/tests/Makefile.am | 2 +- src/protocols/rdp/Makefile.am | 6 +++--- src/protocols/rdp/tests/Makefile.am | 2 +- src/protocols/ssh/Makefile.am | 2 +- src/protocols/telnet/Makefile.am | 2 +- src/protocols/vnc/Makefile.am | 2 +- src/pulse/Makefile.am | 2 +- src/terminal/Makefile.am | 2 +- 22 files changed, 44 insertions(+), 27 deletions(-) diff --git a/README-unit-testing.md b/README-unit-testing.md index 8b4e77687..e76c42070 100644 --- a/README-unit-testing.md +++ b/README-unit-testing.md @@ -46,7 +46,7 @@ modified to contain a sections like the following: ...all source files... test_myproj_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ ...other flags... test_myproj_LDADD = \ diff --git a/configure.ac b/configure.ac index 5639da83e..1a211e550 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_PREREQ([2.61]) AC_INIT([guacamole-server], [1.5.3]) AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) +AM_INIT_AUTOMAKE([-Wall -Werror -Wexpansion-to-defined foreign subdir-objects]) AM_SILENT_RULES([yes]) LT_PREREQ([2.2]) @@ -689,7 +689,7 @@ if test "x$with_rdp" != "xno" then have_freerdp2=yes PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2], - [CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"] + [CPPFLAGS="${RDP_CFLAGS} -Werror -Wexpansion-to-defined $CPPFLAGS"] [AS_IF([test "x${FREERDP2_PLUGIN_DIR}" = "x"], [FREERDP2_PLUGIN_DIR="`$PKG_CONFIG --variable=libdir freerdp2`/freerdp2"])], [AC_MSG_WARN([ diff --git a/scripts/build-guac.sh b/scripts/build-guac.sh index 65175660d..c917aa521 100644 --- a/scripts/build-guac.sh +++ b/scripts/build-guac.sh @@ -3,6 +3,10 @@ cd guacamole-server +echo looking for it... +find . -name 'Makefile.am' +echo looked for it... + export PKG_CONFIG_PATH="/quasi-msys2/root/ucrt64/lib/pkgconfig/" export LDFLAGS="-L/quasi-msys2/root/ucrt64/bin/" export CFLAGS="-I/quasi-msys2/root/ucrt64/include/ -I/usr/x86_64-w64-mingw32/include" diff --git a/scripts/install-msys2.sh b/scripts/install-msys2.sh index 273879707..4f300c03a 100644 --- a/scripts/install-msys2.sh +++ b/scripts/install-msys2.sh @@ -17,8 +17,21 @@ make install \ mingw-w64-ucrt-x86_64-freerdp \ mingw-w64-ucrt-x86_64-libvncserver \ mingw-w64-ucrt-x86_64-dlfcn \ - mingw-w64-ucrt-x86_64-libgcrypt + mingw-w64-ucrt-x86_64-libgcrypt \ + mingw-w64-ucrt-x86_64-libwebsockets \ + mingw-w64-ucrt-x86_64-libwebp \ + mingw-w64-ucrt-x86_64-openssl \ + mingw-w64-ucrt-x86_64-libvorbis \ + mingw-w64-ucrt-x86_64-pulseaudio + +ln -s /quasi-msys2/root/ucrt64/bin/libcairo-2.dll /quasi-msys2/root/ucrt64/bin/libcairo.dll +ln -s /quasi-msys2/root/ucrt64/bin/libjpeg-8.dll /quasi-msys2/root/ucrt64/bin/libjpeg.dll +ln -s /quasi-msys2/root/ucrt64/bin/libpng16-16.dll /quasi-msys2/root/ucrt64/bin/libpng.dll +ln -s /quasi-msys2/root/ucrt64/bin/libcrypto-3-x64.dll /quasi-msys2/root/ucrt64/bin/libcrypto.dll +ln -s /quasi-msys2/root/ucrt64/bin/libssl-3-x64.dll /quasi-msys2/root/ucrt64/bin/libssl.dll +ln -s /quasi-msys2/root/ucrt64/bin/libwebp-7.dll /quasi-msys2/root/ucrt64/bin/libwebp.dll +ln -s /quasi-msys2/root/ucrt64/bin/libpulse-0.dll /quasi-msys2/root/ucrt64/bin/libpulse.dll +ln -s /quasi-msys2/root/ucrt64/bin/libvorbis-0.dll /quasi-msys2/root/ucrt64/bin/libvorbis.dll +ln -s /quasi-msys2/root/ucrt64/bin/libvorbisenc-2.dll /quasi-msys2/root/ucrt64/bin/libvorbisenc.dll +ln -s /quasi-msys2/root/ucrt64/bin/libogg-0.dll /quasi-msys2/root/ucrt64/bin/libogg.dll -ln -s /quasi-msys2/root/ucrt64/bin/libcairo-2.dll /quasi-msys2/root/ucrt64/bin/libcairo.dll -ln -s /quasi-msys2/root/ucrt64/bin/libjpeg-8.dll /quasi-msys2/root/ucrt64/bin/libjpeg.dll -ln -s /quasi-msys2/root/ucrt64/bin/libpng16-16.dll /quasi-msys2/root/ucrt64/bin/libpng.dll diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am index 50abb2c13..a15c85d4a 100644 --- a/src/common-ssh/Makefile.am +++ b/src/common-ssh/Makefile.am @@ -44,7 +44,7 @@ noinst_HEADERS = \ common-ssh/user.h libguac_common_ssh_la_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common-ssh/tests/Makefile.am b/src/common-ssh/tests/Makefile.am index 12396991a..58a89aa03 100644 --- a/src/common-ssh/tests/Makefile.am +++ b/src/common-ssh/tests/Makefile.am @@ -37,7 +37,7 @@ test_common_ssh_SOURCES = \ sftp/normalize_path.c test_common_ssh_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 947104aeb..f5be53671 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -63,7 +63,7 @@ libguac_common_la_SOURCES = \ surface.c libguac_common_la_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @LIBGUAC_INCLUDE@ libguac_common_la_LIBADD = \ diff --git a/src/common/tests/Makefile.am b/src/common/tests/Makefile.am index 526577b21..fca720ec6 100644 --- a/src/common/tests/Makefile.am +++ b/src/common/tests/Makefile.am @@ -49,7 +49,7 @@ test_common_SOURCES = \ string/split.c test_common_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @COMMON_INCLUDE@ test_common_LDADD = \ diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index 42a445845..e9ba09450 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -60,7 +60,7 @@ guacd_SOURCES += move-pipe.c endif guacd_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/guacenc/Makefile.am b/src/guacenc/Makefile.am index 34aeaf6a5..eefb5e5f6 100644 --- a/src/guacenc/Makefile.am +++ b/src/guacenc/Makefile.am @@ -88,7 +88,7 @@ noinst_HEADERS += webp.h endif guacenc_CFLAGS = \ - -Werror -Wall \ + -Werror -Wexpansion-to-defined -Wall \ @AVCODEC_CFLAGS@ \ @AVFORMAT_CFLAGS@ \ @AVUTIL_CFLAGS@ \ diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am index af5c5a762..1c73fde1d 100644 --- a/src/guaclog/Makefile.am +++ b/src/guaclog/Makefile.am @@ -48,7 +48,7 @@ guaclog_SOURCES = \ state.c guaclog_CFLAGS = \ - -Werror -Wall \ + -Werror -Wexpansion-to-defined -Wall \ @LIBGUAC_INCLUDE@ guaclog_LDADD = \ diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 0ad06676a..12ae1a069 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -148,7 +148,7 @@ libguacinc_HEADERS += guacamole/wait-handle.h endif libguac_la_CFLAGS = \ - -Werror -Wall -pedantic + -Werror -Wexpansion-to-defined -Wall -pedantic libguac_la_LDFLAGS = \ -version-info 22:0:1 \ diff --git a/src/libguac/tests/Makefile.am b/src/libguac/tests/Makefile.am index b7452cf01..e4d03da12 100644 --- a/src/libguac/tests/Makefile.am +++ b/src/libguac/tests/Makefile.am @@ -56,7 +56,7 @@ test_libguac_SOURCES = \ test_libguac_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @LIBGUAC_INCLUDE@ test_libguac_LDADD = \ diff --git a/src/protocols/kubernetes/Makefile.am b/src/protocols/kubernetes/Makefile.am index e488a83d9..b72d2ff5a 100644 --- a/src/protocols/kubernetes/Makefile.am +++ b/src/protocols/kubernetes/Makefile.am @@ -56,7 +56,7 @@ noinst_HEADERS = \ user.h libguac_client_kubernetes_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/kubernetes/tests/Makefile.am b/src/protocols/kubernetes/tests/Makefile.am index ec0c3a8b6..4aa882ed7 100644 --- a/src/protocols/kubernetes/tests/Makefile.am +++ b/src/protocols/kubernetes/tests/Makefile.am @@ -38,7 +38,7 @@ test_kubernetes_SOURCES = \ url/escape.c test_kubernetes_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @LIBGUAC_CLIENT_KUBERNETES_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index b8dd53479..caedeac34 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -134,7 +134,7 @@ noinst_HEADERS = \ user.h libguac_client_rdp_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @@ -169,7 +169,7 @@ libguac_common_svc_client_la_SOURCES = \ plugins/guac-common-svc/guac-common-svc.c libguac_common_svc_client_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @LIBGUAC_INCLUDE@ \ @RDP_CFLAGS@ @@ -192,7 +192,7 @@ libguacai_client_la_SOURCES = \ plugins/ptr-string.c libguacai_client_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ diff --git a/src/protocols/rdp/tests/Makefile.am b/src/protocols/rdp/tests/Makefile.am index 33136696d..40dcb8777 100644 --- a/src/protocols/rdp/tests/Makefile.am +++ b/src/protocols/rdp/tests/Makefile.am @@ -38,7 +38,7 @@ test_rdp_SOURCES = \ fs/normalize_path.c test_rdp_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @LIBGUAC_CLIENT_RDP_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am index d586315fb..07eae84df 100644 --- a/src/protocols/ssh/Makefile.am +++ b/src/protocols/ssh/Makefile.am @@ -59,7 +59,7 @@ noinst_HEADERS += ssh_agent.h endif libguac_client_ssh_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/telnet/Makefile.am b/src/protocols/telnet/Makefile.am index 1b78b1393..06b20fb9a 100644 --- a/src/protocols/telnet/Makefile.am +++ b/src/protocols/telnet/Makefile.am @@ -49,7 +49,7 @@ noinst_HEADERS = \ user.h libguac_client_telnet_la_CFLAGS = \ - -Werror -Wall -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -Iinclude \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index b19d123ab..c7a307fac 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -55,7 +55,7 @@ noinst_HEADERS = \ vnc.h libguac_client_vnc_la_CFLAGS = \ - -Werror -Wall -pedantic -Iinclude \ + -Werror -Wexpansion-to-defined -Wall -pedantic -Iinclude \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ diff --git a/src/pulse/Makefile.am b/src/pulse/Makefile.am index 7ed891f10..ca97fa824 100644 --- a/src/pulse/Makefile.am +++ b/src/pulse/Makefile.am @@ -35,7 +35,7 @@ libguac_pulse_la_SOURCES = \ pulse.c libguac_pulse_la_CFLAGS = \ - -Werror -Wall -pedantic \ + -Werror -Wexpansion-to-defined -Wall -pedantic \ @LIBGUAC_INCLUDE@ libguac_pulse_la_LIBADD = \ diff --git a/src/terminal/Makefile.am b/src/terminal/Makefile.am index 89122b930..a60085f34 100644 --- a/src/terminal/Makefile.am +++ b/src/terminal/Makefile.am @@ -66,7 +66,7 @@ libguac_terminal_la_SOURCES = \ xparsecolor.c libguac_terminal_la_CFLAGS = \ - -Werror -Wall \ + -Werror -Wexpansion-to-defined -Wall \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @PANGO_CFLAGS@ \ From 47931d517b36ff7675f31daa5c7489d7c3adce47 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 16 Nov 2023 18:58:03 +0000 Subject: [PATCH 14/15] Fix build. --- README-unit-testing.md | 2 +- configure.ac | 5 ++++- scripts/build-guac.sh | 4 ---- src/common-ssh/Makefile.am | 3 ++- src/common-ssh/tests/Makefile.am | 3 ++- src/common/Makefile.am | 3 ++- src/common/tests/Makefile.am | 3 ++- src/guacd/Makefile.am | 3 ++- src/guacenc/Makefile.am | 3 ++- src/guaclog/Makefile.am | 3 ++- src/libguac/Makefile.am | 3 ++- src/libguac/tests/Makefile.am | 3 ++- src/protocols/kubernetes/Makefile.am | 3 ++- src/protocols/kubernetes/tests/Makefile.am | 3 ++- src/protocols/rdp/Makefile.am | 9 ++++++--- src/protocols/rdp/tests/Makefile.am | 3 ++- src/protocols/ssh/Makefile.am | 3 ++- src/protocols/telnet/Makefile.am | 3 ++- src/protocols/vnc/Makefile.am | 3 ++- src/pulse/Makefile.am | 5 +++-- src/terminal/Makefile.am | 4 +++- 21 files changed, 47 insertions(+), 27 deletions(-) diff --git a/README-unit-testing.md b/README-unit-testing.md index e76c42070..8b4e77687 100644 --- a/README-unit-testing.md +++ b/README-unit-testing.md @@ -46,7 +46,7 @@ modified to contain a sections like the following: ...all source files... test_myproj_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ ...other flags... test_myproj_LDADD = \ diff --git a/configure.ac b/configure.ac index 1a211e550..3b6d7e6f8 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_PREREQ([2.61]) AC_INIT([guacamole-server], [1.5.3]) AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE([-Wall -Werror -Wexpansion-to-defined foreign subdir-objects]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_SILENT_RULES([yes]) LT_PREREQ([2.2]) @@ -42,6 +42,7 @@ AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h time.h sys/time.h syslo # Cygwin build CYWGIN_LDFLAGS= +CYGWIN_CFLAGS= AC_ARG_WITH([cygwin], [AS_HELP_STRING([--with-cygwin], [use Cygwin to build under Windows @<:@default=no@:>@])], @@ -52,6 +53,7 @@ AM_CONDITIONAL([CYGWIN_BUILD], [test "x${with_cygwin}" = "xyes"]) if test "x$with_cygwin" = "xyes" then CYWGIN_LDFLAGS=-no-undefined + CYGWIN_CFLAGS=-Wno-error=expansion-to-defined AC_DEFINE([CYGWIN_BUILD],,[Build against Cygwin on Windows]) fi @@ -154,6 +156,7 @@ AC_SUBST(PTHREAD_LIBS) AC_SUBST(UUID_LIBS) AC_SUBST(CUNIT_LIBS) AC_SUBST(CYWGIN_LDFLAGS) +AC_SUBST(CYGWIN_CFLAGS) # Library functions AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset select strdup nanosleep]) diff --git a/scripts/build-guac.sh b/scripts/build-guac.sh index c917aa521..65175660d 100644 --- a/scripts/build-guac.sh +++ b/scripts/build-guac.sh @@ -3,10 +3,6 @@ cd guacamole-server -echo looking for it... -find . -name 'Makefile.am' -echo looked for it... - export PKG_CONFIG_PATH="/quasi-msys2/root/ucrt64/lib/pkgconfig/" export LDFLAGS="-L/quasi-msys2/root/ucrt64/bin/" export CFLAGS="-I/quasi-msys2/root/ucrt64/include/ -I/usr/x86_64-w64-mingw32/include" diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am index a15c85d4a..19a0333ad 100644 --- a/src/common-ssh/Makefile.am +++ b/src/common-ssh/Makefile.am @@ -44,7 +44,8 @@ noinst_HEADERS = \ common-ssh/user.h libguac_common_ssh_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common-ssh/tests/Makefile.am b/src/common-ssh/tests/Makefile.am index 58a89aa03..b5d330bf4 100644 --- a/src/common-ssh/tests/Makefile.am +++ b/src/common-ssh/tests/Makefile.am @@ -37,7 +37,8 @@ test_common_ssh_SOURCES = \ sftp/normalize_path.c test_common_ssh_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index f5be53671..6baa4aea8 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -63,7 +63,8 @@ libguac_common_la_SOURCES = \ surface.c libguac_common_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ libguac_common_la_LIBADD = \ diff --git a/src/common/tests/Makefile.am b/src/common/tests/Makefile.am index fca720ec6..049397a45 100644 --- a/src/common/tests/Makefile.am +++ b/src/common/tests/Makefile.am @@ -49,7 +49,8 @@ test_common_SOURCES = \ string/split.c test_common_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ test_common_LDADD = \ diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index e9ba09450..c1ea25810 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -60,7 +60,8 @@ guacd_SOURCES += move-pipe.c endif guacd_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/guacenc/Makefile.am b/src/guacenc/Makefile.am index eefb5e5f6..34c0e6798 100644 --- a/src/guacenc/Makefile.am +++ b/src/guacenc/Makefile.am @@ -88,7 +88,8 @@ noinst_HEADERS += webp.h endif guacenc_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall \ + -Werror -Wall \ + @CYGWIN_CFLAGS@ \ @AVCODEC_CFLAGS@ \ @AVFORMAT_CFLAGS@ \ @AVUTIL_CFLAGS@ \ diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am index 1c73fde1d..8cc9a7222 100644 --- a/src/guaclog/Makefile.am +++ b/src/guaclog/Makefile.am @@ -48,7 +48,8 @@ guaclog_SOURCES = \ state.c guaclog_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall \ + -Werror -Wall \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ guaclog_LDADD = \ diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 12ae1a069..08f21d8ac 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -148,7 +148,8 @@ libguacinc_HEADERS += guacamole/wait-handle.h endif libguac_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic + @CYGWIN_CFLAGS@ \ + -Werror -Wall -pedantic libguac_la_LDFLAGS = \ -version-info 22:0:1 \ diff --git a/src/libguac/tests/Makefile.am b/src/libguac/tests/Makefile.am index e4d03da12..68943da90 100644 --- a/src/libguac/tests/Makefile.am +++ b/src/libguac/tests/Makefile.am @@ -56,7 +56,8 @@ test_libguac_SOURCES = \ test_libguac_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ test_libguac_LDADD = \ diff --git a/src/protocols/kubernetes/Makefile.am b/src/protocols/kubernetes/Makefile.am index b72d2ff5a..5b51d58cf 100644 --- a/src/protocols/kubernetes/Makefile.am +++ b/src/protocols/kubernetes/Makefile.am @@ -56,7 +56,8 @@ noinst_HEADERS = \ user.h libguac_client_kubernetes_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/kubernetes/tests/Makefile.am b/src/protocols/kubernetes/tests/Makefile.am index 4aa882ed7..1d1a8f443 100644 --- a/src/protocols/kubernetes/tests/Makefile.am +++ b/src/protocols/kubernetes/tests/Makefile.am @@ -38,7 +38,8 @@ test_kubernetes_SOURCES = \ url/escape.c test_kubernetes_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_CLIENT_KUBERNETES_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index caedeac34..6fcab1f4d 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -134,7 +134,8 @@ noinst_HEADERS = \ user.h libguac_client_rdp_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @@ -169,7 +170,8 @@ libguac_common_svc_client_la_SOURCES = \ plugins/guac-common-svc/guac-common-svc.c libguac_common_svc_client_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ \ @RDP_CFLAGS@ @@ -192,7 +194,8 @@ libguacai_client_la_SOURCES = \ plugins/ptr-string.c libguacai_client_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ diff --git a/src/protocols/rdp/tests/Makefile.am b/src/protocols/rdp/tests/Makefile.am index 40dcb8777..41206a1e5 100644 --- a/src/protocols/rdp/tests/Makefile.am +++ b/src/protocols/rdp/tests/Makefile.am @@ -38,7 +38,8 @@ test_rdp_SOURCES = \ fs/normalize_path.c test_rdp_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_CLIENT_RDP_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am index 07eae84df..27aebcc4d 100644 --- a/src/protocols/ssh/Makefile.am +++ b/src/protocols/ssh/Makefile.am @@ -59,7 +59,8 @@ noinst_HEADERS += ssh_agent.h endif libguac_client_ssh_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/telnet/Makefile.am b/src/protocols/telnet/Makefile.am index 06b20fb9a..9c3283496 100644 --- a/src/protocols/telnet/Makefile.am +++ b/src/protocols/telnet/Makefile.am @@ -49,7 +49,8 @@ noinst_HEADERS = \ user.h libguac_client_telnet_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -Iinclude \ + -Werror -Wall -Iinclude \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index c7a307fac..517de2b5c 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -55,7 +55,8 @@ noinst_HEADERS = \ vnc.h libguac_client_vnc_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic -Iinclude \ + -Werror -Wall -pedantic -Iinclude \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ diff --git a/src/pulse/Makefile.am b/src/pulse/Makefile.am index ca97fa824..0a9b066cd 100644 --- a/src/pulse/Makefile.am +++ b/src/pulse/Makefile.am @@ -34,8 +34,9 @@ noinst_HEADERS = \ libguac_pulse_la_SOURCES = \ pulse.c -libguac_pulse_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall -pedantic \ +libguac_pulse_la_CFLAGS = \ + -Werror -Wall -pedantic \ + @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ libguac_pulse_la_LIBADD = \ diff --git a/src/terminal/Makefile.am b/src/terminal/Makefile.am index a60085f34..23d23d732 100644 --- a/src/terminal/Makefile.am +++ b/src/terminal/Makefile.am @@ -66,7 +66,9 @@ libguac_terminal_la_SOURCES = \ xparsecolor.c libguac_terminal_la_CFLAGS = \ - -Werror -Wexpansion-to-defined -Wall \ + -Werror -Wall \ + @CYGWIN_CFLAGS@ \ + @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @PANGO_CFLAGS@ \ From c46e4999715d905b0e28e451a686a21183ebc711 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 16 Nov 2023 22:00:57 +0000 Subject: [PATCH 15/15] Buildy buildy --- configure.ac | 4 ++-- src/common-ssh/Makefile.am | 1 - src/common-ssh/tests/Makefile.am | 1 - src/common/Makefile.am | 1 - src/common/tests/Makefile.am | 1 - src/guacd/Makefile.am | 1 - src/libguac/Makefile.am | 13 ++++++++----- src/libguac/client.c | 9 +++++---- src/libguac/guacamole/client.h | 2 ++ src/libguac/guacamole/socket-handle.h | 0 src/libguac/id.c | 5 +++-- src/libguac/tests/Makefile.am | 1 - src/libguac/wait-fd.c | 5 +++-- src/libguac/wol.c | 8 +++++++- src/protocols/kubernetes/tests/Makefile.am | 1 - src/protocols/rdp/tests/Makefile.am | 1 - src/protocols/vnc/Makefile.am | 1 - src/pulse/Makefile.am | 1 - 18 files changed, 30 insertions(+), 26 deletions(-) mode change 100755 => 100644 src/libguac/guacamole/socket-handle.h diff --git a/configure.ac b/configure.ac index 3b6d7e6f8..72f773507 100644 --- a/configure.ac +++ b/configure.ac @@ -42,7 +42,7 @@ AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h time.h sys/time.h syslo # Cygwin build CYWGIN_LDFLAGS= -CYGWIN_CFLAGS= +CYGWIN_CFLAGS=-Werror -Wall -pedantic AC_ARG_WITH([cygwin], [AS_HELP_STRING([--with-cygwin], [use Cygwin to build under Windows @<:@default=no@:>@])], @@ -53,7 +53,7 @@ AM_CONDITIONAL([CYGWIN_BUILD], [test "x${with_cygwin}" = "xyes"]) if test "x$with_cygwin" = "xyes" then CYWGIN_LDFLAGS=-no-undefined - CYGWIN_CFLAGS=-Wno-error=expansion-to-defined + CYGWIN_CFLAGS=-Werror -Wall -Wno-error=expansion-to-defined AC_DEFINE([CYGWIN_BUILD],,[Build against Cygwin on Windows]) fi diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am index 19a0333ad..23dcb073a 100644 --- a/src/common-ssh/Makefile.am +++ b/src/common-ssh/Makefile.am @@ -44,7 +44,6 @@ noinst_HEADERS = \ common-ssh/user.h libguac_common_ssh_la_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common-ssh/tests/Makefile.am b/src/common-ssh/tests/Makefile.am index b5d330bf4..3c77a6c39 100644 --- a/src/common-ssh/tests/Makefile.am +++ b/src/common-ssh/tests/Makefile.am @@ -37,7 +37,6 @@ test_common_ssh_SOURCES = \ sftp/normalize_path.c test_common_ssh_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 6baa4aea8..380d7dd99 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -63,7 +63,6 @@ libguac_common_la_SOURCES = \ surface.c libguac_common_la_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ diff --git a/src/common/tests/Makefile.am b/src/common/tests/Makefile.am index 049397a45..ca39288e9 100644 --- a/src/common/tests/Makefile.am +++ b/src/common/tests/Makefile.am @@ -49,7 +49,6 @@ test_common_SOURCES = \ string/split.c test_common_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index c1ea25810..59eb8591d 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -60,7 +60,6 @@ guacd_SOURCES += move-pipe.c endif guacd_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 08f21d8ac..5f1aaf64e 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -77,6 +77,7 @@ libguacinc_HEADERS = \ guacamole/user-constants.h \ guacamole/user-fntypes.h \ guacamole/user-types.h \ + guacamole/wait-fd.h \ guacamole/wol.h \ guacamole/wol-constants.h @@ -85,8 +86,7 @@ noinst_HEADERS = \ encode-png.h \ palette.h \ user-handlers.h \ - raw_encoder.h \ - wait-fd.h + raw_encoder.h libguac_la_SOURCES = \ argv.c \ @@ -116,7 +116,7 @@ libguac_la_SOURCES = \ user.c \ user-handlers.c \ user-handshake.c \ - wait-fd.c \ + wait-fd.c \ wol.c # Compile WebP support if available @@ -145,11 +145,14 @@ libguac_la_SOURCES += wait-handle.c libguacinc_HEADERS += guacamole/handle-helpers.h libguacinc_HEADERS += guacamole/socket-handle.h libguacinc_HEADERS += guacamole/wait-handle.h +else +libguac_la_SOURCES += wait-fd.c +libguac_la_SOURCES += socket-fd.c +libguacinc_HEADERS += guacamole/wait-fd.h endif libguac_la_CFLAGS = \ - @CYGWIN_CFLAGS@ \ - -Werror -Wall -pedantic + @CYGWIN_CFLAGS@ libguac_la_LDFLAGS = \ -version-info 22:0:1 \ diff --git a/src/libguac/client.c b/src/libguac/client.c index 6c3b3aedd..84adf9a82 100644 --- a/src/libguac/client.c +++ b/src/libguac/client.c @@ -48,6 +48,7 @@ #ifdef CYGWIN_BUILD #include +#include #endif /** @@ -518,13 +519,13 @@ static int guac_client_start_pending_users_timer(guac_client* client) { #ifdef CYGWIN_BUILD if (!CreateTimerQueueTimer( - &(client->__pending_users_timer)), + &(client->__pending_users_timer), NULL, guac_client_promote_pending_users, client, GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL, - GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL - 0) { + GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL, + 0)) { // oh noes error return 1; @@ -545,7 +546,6 @@ static int guac_client_start_pending_users_timer(guac_client* client) { pthread_mutex_unlock(&(client->__pending_users_timer_mutex)); return 1; } -#endif /* Configure the pending users timer to run on the defined interval */ struct itimerspec time_config = { @@ -560,6 +560,7 @@ static int guac_client_start_pending_users_timer(guac_client* client) { pthread_mutex_unlock(&(client->__pending_users_timer_mutex)); return 1; } +#endif /* Mark the timer as registered but not yet running */ client->__pending_users_timer_state = GUAC_CLIENT_PENDING_TIMER_REGISTERED; diff --git a/src/libguac/guacamole/client.h b/src/libguac/guacamole/client.h index 9da16e2ab..89df8bdaf 100644 --- a/src/libguac/guacamole/client.h +++ b/src/libguac/guacamole/client.h @@ -26,6 +26,8 @@ * @file client.h */ +#include "config.h" + #include "client-fntypes.h" #include "client-types.h" #include "client-constants.h" diff --git a/src/libguac/guacamole/socket-handle.h b/src/libguac/guacamole/socket-handle.h old mode 100755 new mode 100644 diff --git a/src/libguac/id.c b/src/libguac/id.c index c42bdff81..10b447640 100644 --- a/src/libguac/id.c +++ b/src/libguac/id.c @@ -24,6 +24,7 @@ #ifdef CYGWIN_BUILD +#include #include #elif defined(HAVE_LIBUUID) #include @@ -58,7 +59,7 @@ char* guac_generate_id(char prefix) { /* Convert the UUID to an all-caps, null-terminated tring */ RPC_CSTR uuid_string; - if (UuidToString(uuid, &uuid_string) == RPC_S_OUT_OF_MEMORY) { + if (UuidToString(&uuid, &uuid_string) == RPC_S_OUT_OF_MEMORY) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for unique ID"; return NULL; @@ -68,7 +69,7 @@ char* guac_generate_id(char prefix) { for (int i = 0; i < GUAC_UUID_LEN; i++) identifier[i] = tolower(uuid_string[i]); - RpcStringFree(uuid_string); + RpcStringFree(&uuid_string); #else diff --git a/src/libguac/tests/Makefile.am b/src/libguac/tests/Makefile.am index 68943da90..79180c6ec 100644 --- a/src/libguac/tests/Makefile.am +++ b/src/libguac/tests/Makefile.am @@ -56,7 +56,6 @@ test_libguac_SOURCES = \ test_libguac_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@ diff --git a/src/libguac/wait-fd.c b/src/libguac/wait-fd.c index d6079be38..0a41e8d14 100644 --- a/src/libguac/wait-fd.c +++ b/src/libguac/wait-fd.c @@ -19,8 +19,9 @@ #include "config.h" -#ifdef ENABLE_WINSOCK -# include +#ifdef CYGWIN_BUILD +#include +#include #else # ifdef HAVE_POLL # include diff --git a/src/libguac/wol.c b/src/libguac/wol.c index 9d69306c9..22c9fb72a 100644 --- a/src/libguac/wol.c +++ b/src/libguac/wol.c @@ -26,9 +26,15 @@ #include #include #include + +#ifdef CYGWIN_BUILD +#include +#include +#else #include #include #include +#endif /** * Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address @@ -195,4 +201,4 @@ int guac_wol_wake(const char* mac_addr, const char* broadcast_addr, return 0; return -1; -} \ No newline at end of file +} diff --git a/src/protocols/kubernetes/tests/Makefile.am b/src/protocols/kubernetes/tests/Makefile.am index 1d1a8f443..b1936b612 100644 --- a/src/protocols/kubernetes/tests/Makefile.am +++ b/src/protocols/kubernetes/tests/Makefile.am @@ -38,7 +38,6 @@ test_kubernetes_SOURCES = \ url/escape.c test_kubernetes_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @LIBGUAC_CLIENT_KUBERNETES_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/rdp/tests/Makefile.am b/src/protocols/rdp/tests/Makefile.am index 41206a1e5..da6cbd75d 100644 --- a/src/protocols/rdp/tests/Makefile.am +++ b/src/protocols/rdp/tests/Makefile.am @@ -38,7 +38,6 @@ test_rdp_SOURCES = \ fs/normalize_path.c test_rdp_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @LIBGUAC_CLIENT_RDP_INCLUDE@ \ @LIBGUAC_INCLUDE@ diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index 517de2b5c..e7a3f4b70 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -55,7 +55,6 @@ noinst_HEADERS = \ vnc.h libguac_client_vnc_la_CFLAGS = \ - -Werror -Wall -pedantic -Iinclude \ @CYGWIN_CFLAGS@ \ @COMMON_INCLUDE@ \ @COMMON_SSH_INCLUDE@ \ diff --git a/src/pulse/Makefile.am b/src/pulse/Makefile.am index 0a9b066cd..4bef43474 100644 --- a/src/pulse/Makefile.am +++ b/src/pulse/Makefile.am @@ -35,7 +35,6 @@ libguac_pulse_la_SOURCES = \ pulse.c libguac_pulse_la_CFLAGS = \ - -Werror -Wall -pedantic \ @CYGWIN_CFLAGS@ \ @LIBGUAC_INCLUDE@