GUACAMOLE-574: Add support for reading STDIN from a pipe stream.
This commit is contained in:
parent
f3d9c2f610
commit
97593958e4
@ -48,6 +48,7 @@ libguac_terminal_la_SOURCES = \
|
|||||||
select.c \
|
select.c \
|
||||||
terminal.c \
|
terminal.c \
|
||||||
terminal_handlers.c \
|
terminal_handlers.c \
|
||||||
|
terminal-stdin-stream.c \
|
||||||
typescript.c \
|
typescript.c \
|
||||||
xparsecolor.c
|
xparsecolor.c
|
||||||
|
|
||||||
|
156
src/terminal/terminal-stdin-stream.c
Normal file
156
src/terminal/terminal-stdin-stream.c
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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 "terminal/common.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for "blob" instructions which writes the data of received
|
||||||
|
* blobs to STDIN of the terminal associated with the stream.
|
||||||
|
*
|
||||||
|
* @see guac_user_blob_handler
|
||||||
|
*/
|
||||||
|
static int guac_terminal_input_stream_blob_handler(guac_user* user,
|
||||||
|
guac_stream* stream, void* data, int length) {
|
||||||
|
|
||||||
|
guac_terminal* term = (guac_terminal*) stream->data;
|
||||||
|
|
||||||
|
/* Attempt to write received data */
|
||||||
|
guac_terminal_lock(term);
|
||||||
|
int result = guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
|
||||||
|
guac_terminal_unlock(term);
|
||||||
|
|
||||||
|
/* Acknowledge receipt of data and result of write attempt */
|
||||||
|
if (result <= 0) {
|
||||||
|
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||||
|
"Attempt to write to STDIN via an inbound stream failed.");
|
||||||
|
|
||||||
|
guac_protocol_send_ack(user->socket, stream,
|
||||||
|
"Attempt to write to STDIN failed.",
|
||||||
|
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||||
|
"%i bytes successfully written to STDIN from an inbound stream.",
|
||||||
|
length);
|
||||||
|
|
||||||
|
guac_protocol_send_ack(user->socket, stream,
|
||||||
|
"Data written to STDIN.",
|
||||||
|
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for "end" instructions which disassociates the given
|
||||||
|
* stream from the terminal, allowing user input to resume.
|
||||||
|
*
|
||||||
|
* @see guac_user_end_handler
|
||||||
|
*/
|
||||||
|
static int guac_terminal_input_stream_end_handler(guac_user* user,
|
||||||
|
guac_stream* stream) {
|
||||||
|
|
||||||
|
guac_terminal* term = (guac_terminal*) stream->data;
|
||||||
|
|
||||||
|
/* Reset input stream, unblocking user input */
|
||||||
|
guac_terminal_lock(term);
|
||||||
|
term->input_stream = NULL;
|
||||||
|
guac_terminal_unlock(term);
|
||||||
|
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG, "Inbound stream closed. User input "
|
||||||
|
"will now resume affecting STDIN.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation of guac_terminal_send_stream() which assumes
|
||||||
|
* that the guac_terminal has already been locked through a call to
|
||||||
|
* guac_terminal_lock(). The semantics of all parameters and the return
|
||||||
|
* value are identical to guac_terminal_send_stream().
|
||||||
|
*
|
||||||
|
* @see guac_terminal_send_stream()
|
||||||
|
*/
|
||||||
|
static int __guac_terminal_send_stream(guac_terminal* term, guac_user* user,
|
||||||
|
guac_stream* stream) {
|
||||||
|
|
||||||
|
/* If a stream is already being used for STDIN, deny creation of
|
||||||
|
* further streams */
|
||||||
|
if (term->input_stream != NULL) {
|
||||||
|
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG, "Attempt to direct the contents "
|
||||||
|
"of an inbound stream to STDIN denied. STDIN is already "
|
||||||
|
"being read from an inbound stream.");
|
||||||
|
|
||||||
|
guac_protocol_send_ack(user->socket, stream,
|
||||||
|
"STDIN is already being read from a stream.",
|
||||||
|
GUAC_PROTOCOL_STATUS_RESOURCE_CONFLICT);
|
||||||
|
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG, "Now reading STDIN from inbound "
|
||||||
|
"stream. User input will no longer affect STDIN until the "
|
||||||
|
"stream is closed.");
|
||||||
|
|
||||||
|
stream->blob_handler = guac_terminal_input_stream_blob_handler;
|
||||||
|
stream->end_handler = guac_terminal_input_stream_end_handler;
|
||||||
|
stream->data = term;
|
||||||
|
|
||||||
|
/* Block user input until stream is ended */
|
||||||
|
term->input_stream = stream;
|
||||||
|
|
||||||
|
/* Acknowledge redirection from stream */
|
||||||
|
guac_protocol_send_ack(user->socket, stream,
|
||||||
|
"Now reading STDIN from stream.",
|
||||||
|
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||||
|
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_terminal_send_stream(guac_terminal* term, guac_user* user,
|
||||||
|
guac_stream* stream) {
|
||||||
|
|
||||||
|
int result;
|
||||||
|
|
||||||
|
guac_terminal_lock(term);
|
||||||
|
result = __guac_terminal_send_stream(term, user, stream);
|
||||||
|
guac_terminal_unlock(term);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -604,6 +604,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read input from keyboard by default */
|
||||||
|
term->input_stream = NULL;
|
||||||
|
|
||||||
/* Init pipe stream (output to display by default) */
|
/* Init pipe stream (output to display by default) */
|
||||||
term->pipe_stream = NULL;
|
term->pipe_stream = NULL;
|
||||||
|
|
||||||
@ -1565,11 +1568,23 @@ void guac_terminal_unlock(guac_terminal* terminal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int guac_terminal_send_data(guac_terminal* term, const char* data, int length) {
|
int guac_terminal_send_data(guac_terminal* term, const char* data, int length) {
|
||||||
|
|
||||||
|
/* Block all other sources of input if input is coming from a stream */
|
||||||
|
if (term->input_stream != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
|
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int guac_terminal_send_string(guac_terminal* term, const char* data) {
|
int guac_terminal_send_string(guac_terminal* term, const char* data) {
|
||||||
|
|
||||||
|
/* Block all other sources of input if input is coming from a stream */
|
||||||
|
if (term->input_stream != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data));
|
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
|
static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
|
||||||
@ -1867,6 +1882,10 @@ int guac_terminal_sendf(guac_terminal* term, const char* format, ...) {
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
|
|
||||||
|
/* Block all other sources of input if input is coming from a stream */
|
||||||
|
if (term->input_stream != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Print to buffer */
|
/* Print to buffer */
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
|
written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
|
||||||
|
@ -200,6 +200,14 @@ struct guac_terminal {
|
|||||||
*/
|
*/
|
||||||
int stdin_pipe_fd[2];
|
int stdin_pipe_fd[2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently-open pipe stream from which all terminal input should be
|
||||||
|
* read, if any. If no pipe stream is open, terminal input will be received
|
||||||
|
* through keyboard, clipboard, and mouse events, and this value will be
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
guac_stream* input_stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently-open pipe stream to which all terminal output should be
|
* The currently-open pipe stream to which all terminal output should be
|
||||||
* written, if any. If no pipe stream is open, terminal output will be
|
* written, if any. If no pipe stream is open, terminal output will be
|
||||||
@ -518,9 +526,9 @@ int guac_terminal_render_frame(guac_terminal* terminal);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads from this terminal's STDIN. Input comes from key and mouse events
|
* Reads from this terminal's STDIN. Input comes from key and mouse events
|
||||||
* supplied by calls to guac_terminal_send_key() and
|
* supplied by calls to guac_terminal_send_key(),
|
||||||
* guac_terminal_send_mouse(). If input is not yet available, this function
|
* guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not
|
||||||
* will block.
|
* yet available, this function will block.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
|
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
|
||||||
|
|
||||||
@ -576,17 +584,94 @@ int guac_terminal_printf(guac_terminal* terminal, const char* format, ...);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the given key event, sending data, scrolling, pasting clipboard
|
* Handles the given key event, sending data, scrolling, pasting clipboard
|
||||||
* data, etc. as necessary.
|
* data, etc. as necessary. If terminal input is currently coming from a
|
||||||
|
* stream due to a prior call to guac_terminal_send_stream(), any input
|
||||||
|
* which would normally result from the key event is dropped.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal which should receive the given data on STDIN.
|
||||||
|
*
|
||||||
|
* @param keysym
|
||||||
|
* The X11 keysym of the key that was pressed or released.
|
||||||
|
*
|
||||||
|
* @param pressed
|
||||||
|
* Non-zero if the key represented by the given keysym is currently
|
||||||
|
* pressed, zero if it is released.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the key event was handled successfully, non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed);
|
int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the given mouse event, sending data, scrolling, pasting clipboard
|
* Handles the given mouse event, sending data, scrolling, pasting clipboard
|
||||||
* data, etc. as necessary.
|
* data, etc. as necessary. If terminal input is currently coming from a
|
||||||
|
* stream due to a prior call to guac_terminal_send_stream(), any input
|
||||||
|
* which would normally result from the mouse event is dropped.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal which should receive the given data on STDIN.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user that originated the mouse event.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* The X coordinate of the mouse within the display when the event
|
||||||
|
* occurred, in pixels. This value is not guaranteed to be within the
|
||||||
|
* bounds of the display area.
|
||||||
|
*
|
||||||
|
* @param y
|
||||||
|
* The Y coordinate of the mouse within the display when the event
|
||||||
|
* occurred, in pixels. This value is not guaranteed to be within the
|
||||||
|
* bounds of the display area.
|
||||||
|
*
|
||||||
|
* @param mask
|
||||||
|
* An integer value representing the current state of each button, where
|
||||||
|
* the Nth bit within the integer is set to 1 if and only if the Nth mouse
|
||||||
|
* button is currently pressed. The lowest-order bit is the left mouse
|
||||||
|
* button, followed by the middle button, right button, and finally the up
|
||||||
|
* and down buttons of the scroll wheel.
|
||||||
|
*
|
||||||
|
* @see GUAC_CLIENT_MOUSE_LEFT
|
||||||
|
* @see GUAC_CLIENT_MOUSE_MIDDLE
|
||||||
|
* @see GUAC_CLIENT_MOUSE_RIGHT
|
||||||
|
* @see GUAC_CLIENT_MOUSE_SCROLL_UP
|
||||||
|
* @see GUAC_CLIENT_MOUSE_SCROLL_DOWN
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the mouse event was handled successfully, non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
int guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
||||||
int x, int y, int mask);
|
int x, int y, int mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the handlers of the given guac_stream such that it serves as the
|
||||||
|
* source of input to the terminal. Other input sources will be temporarily
|
||||||
|
* ignored until the stream is closed through receiving an "end" instruction.
|
||||||
|
* If input is already being read from a stream due to a prior call to
|
||||||
|
* guac_terminal_send_stream(), the prior call will remain in effect and this
|
||||||
|
* call will fail.
|
||||||
|
*
|
||||||
|
* Calling this function will overwrite the data member of the given
|
||||||
|
* guac_stream.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal emulator which should receive input from the given stream.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user that opened the stream.
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
* The guac_stream which should serve as the source of input for the
|
||||||
|
* terminal.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the terminal input has successfully been configured to read from
|
||||||
|
* the given stream, non-zero otherwise.
|
||||||
|
*/
|
||||||
|
int guac_terminal_send_stream(guac_terminal* term, guac_user* user,
|
||||||
|
guac_stream* stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a scroll event received from the scrollbar associated with a
|
* Handles a scroll event received from the scrollbar associated with a
|
||||||
* terminal.
|
* terminal.
|
||||||
@ -740,18 +825,66 @@ int guac_terminal_resize(guac_terminal* term, int width, int height);
|
|||||||
void guac_terminal_flush(guac_terminal* terminal);
|
void guac_terminal_flush(guac_terminal* terminal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the given string as if typed by the user.
|
* Sends the given string as if typed by the user. If terminal input is
|
||||||
|
* currently coming from a stream due to a prior call to
|
||||||
|
* guac_terminal_send_stream(), any input which would normally result from
|
||||||
|
* invoking this function is dropped.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal which should receive the given data on STDIN.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The data the terminal should receive on STDIN.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* The size of the given data, in bytes.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of bytes written to STDIN, or a negative value if an error
|
||||||
|
* occurs preventing the data from being written. This should always be
|
||||||
|
* the size of the data given unless data is intentionally dropped.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
|
int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the given string as if typed by the user.
|
* Sends the given string as if typed by the user. If terminal input is
|
||||||
|
* currently coming from a stream due to a prior call to
|
||||||
|
* guac_terminal_send_stream(), any input which would normally result from
|
||||||
|
* invoking this function is dropped.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal which should receive the given data on STDIN.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The data the terminal should receive on STDIN.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of bytes written to STDIN, or a negative value if an error
|
||||||
|
* occurs preventing the data from being written. This should always be
|
||||||
|
* the size of the data given unless data is intentionally dropped.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_send_string(guac_terminal* term, const char* data);
|
int guac_terminal_send_string(guac_terminal* term, const char* data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends data through STDIN as if typed by the user, using the format
|
* Sends data through STDIN as if typed by the user, using the format string
|
||||||
* string given and any args (similar to printf).
|
* given and any args (similar to printf). If terminal input is currently
|
||||||
|
* coming from a stream due to a prior call to guac_terminal_send_stream(), any
|
||||||
|
* input which would normally result from invoking this function is dropped.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal which should receive the given data on STDIN.
|
||||||
|
*
|
||||||
|
* @param format
|
||||||
|
* A printf-style format string describing the data to be received on
|
||||||
|
* STDIN.
|
||||||
|
*
|
||||||
|
* @param ...
|
||||||
|
* Any srguments to use when filling the format string.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of bytes written to STDIN, or a negative value if an error
|
||||||
|
* occurs preventing the data from being written. This should always be
|
||||||
|
* the size of the data given unless data is intentionally dropped.
|
||||||
*/
|
*/
|
||||||
int guac_terminal_sendf(guac_terminal* term, const char* format, ...);
|
int guac_terminal_sendf(guac_terminal* term, const char* format, ...);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user