GUACAMOLE-313: Merge add "guaclog" utility for producing human-readable interpreatations of keys pressed in session recordings.
This commit is contained in:
commit
5f5b4ea8eb
@ -27,6 +27,7 @@ DIST_SUBDIRS = \
|
||||
src/terminal \
|
||||
src/guacd \
|
||||
src/guacenc \
|
||||
src/guaclog \
|
||||
src/pulse \
|
||||
src/protocols/rdp \
|
||||
src/protocols/ssh \
|
||||
@ -75,6 +76,10 @@ if ENABLE_GUACENC
|
||||
SUBDIRS += src/guacenc
|
||||
endif
|
||||
|
||||
if ENABLE_GUACLOG
|
||||
SUBDIRS += src/guaclog
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
.dockerignore \
|
||||
CONTRIBUTING \
|
||||
|
16
configure.ac
16
configure.ac
@ -1191,6 +1191,18 @@ AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
|
||||
-a "x${have_libavutil}" = "xyes" \
|
||||
-a "x${have_libswscale}" = "xyes"])
|
||||
|
||||
#
|
||||
# guaclog
|
||||
#
|
||||
|
||||
AC_ARG_ENABLE([guaclog],
|
||||
[AS_HELP_STRING([--disable-guaclog],
|
||||
[do not build the Guacamole input logging tool])],
|
||||
[],
|
||||
[enable_guaclog=yes])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_GUACLOG], [test "x${enable_guaclog}" = "xyes"])
|
||||
|
||||
#
|
||||
# Output Makefiles
|
||||
#
|
||||
@ -1207,6 +1219,8 @@ AC_CONFIG_FILES([Makefile
|
||||
src/guacd/man/guacd.conf.5
|
||||
src/guacenc/Makefile
|
||||
src/guacenc/man/guacenc.1
|
||||
src/guaclog/Makefile
|
||||
src/guaclog/man/guaclog.1
|
||||
src/pulse/Makefile
|
||||
src/protocols/rdp/Makefile
|
||||
src/protocols/ssh/Makefile
|
||||
@ -1229,6 +1243,7 @@ AM_COND_IF([ENABLE_VNC], [build_vnc=yes], [build_vnc=no])
|
||||
|
||||
AM_COND_IF([ENABLE_GUACD], [build_guacd=yes], [build_guacd=no])
|
||||
AM_COND_IF([ENABLE_GUACENC], [build_guacenc=yes], [build_guacenc=no])
|
||||
AM_COND_IF([ENABLE_GUACLOG], [build_guaclog=yes], [build_guaclog=no])
|
||||
|
||||
#
|
||||
# Init scripts
|
||||
@ -1272,6 +1287,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
||||
|
||||
guacd ...... ${build_guacd}
|
||||
guacenc .... ${build_guacenc}
|
||||
guaclog .... ${build_guaclog}
|
||||
|
||||
Init scripts: ${build_init}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
.\" specific language governing permissions and limitations
|
||||
.\" under the License.
|
||||
.\"
|
||||
.TH guacenc 1 "1 Jun 2017" "version @PACKAGE_VERSION@" "Apache Guacamole"
|
||||
.TH guacenc 1 "26 Jan 2018" "version @PACKAGE_VERSION@" "Apache Guacamole"
|
||||
.
|
||||
.SH NAME
|
||||
guacenc \- Guacamole video encoder
|
||||
@ -75,3 +75,6 @@ Overrides the default behavior of
|
||||
.B guacenc
|
||||
such that input files will be encoded even if they appear to be recordings of
|
||||
in-progress Guacamole sessions.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR guaclog (1)
|
||||
|
5
src/guaclog/.gitignore
vendored
Normal file
5
src/guaclog/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
# Compiled guaclog
|
||||
guaclog
|
||||
guaclog.exe
|
||||
|
53
src/guaclog/Makefile.am
Normal file
53
src/guaclog/Makefile.am
Normal file
@ -0,0 +1,53 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
bin_PROGRAMS = guaclog
|
||||
|
||||
man_MANS = \
|
||||
man/guaclog.1
|
||||
|
||||
noinst_HEADERS = \
|
||||
guaclog.h \
|
||||
instructions.h \
|
||||
interpret.h \
|
||||
keydef.h \
|
||||
log.h \
|
||||
state.h
|
||||
|
||||
guaclog_SOURCES = \
|
||||
guaclog.c \
|
||||
instructions.c \
|
||||
instruction-key.c \
|
||||
interpret.c \
|
||||
keydef.c \
|
||||
log.c \
|
||||
state.c
|
||||
|
||||
guaclog_CFLAGS = \
|
||||
-Werror -Wall \
|
||||
@LIBGUAC_INCLUDE@
|
||||
|
||||
guaclog_LDADD = \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
EXTRA_DIST = \
|
||||
man/guaclog.1.in
|
||||
|
119
src/guaclog/guaclog.c
Normal file
119
src/guaclog/guaclog.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 "guaclog.h"
|
||||
#include "interpret.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Load defaults */
|
||||
bool force = false;
|
||||
|
||||
/* Parse arguments */
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "s:r:f")) != -1) {
|
||||
|
||||
/* -f: Force */
|
||||
if (opt == 'f')
|
||||
force = true;
|
||||
|
||||
/* Invalid option */
|
||||
else {
|
||||
goto invalid_options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Log start */
|
||||
guaclog_log(GUAC_LOG_INFO, "Guacamole input log interpreter (guaclog) "
|
||||
"version " VERSION);
|
||||
|
||||
/* Track number of overall failures */
|
||||
int total_files = argc - optind;
|
||||
int failures = 0;
|
||||
|
||||
/* Abort if no files given */
|
||||
if (total_files <= 0) {
|
||||
guaclog_log(GUAC_LOG_INFO, "No input files specified. Nothing to do.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
guaclog_log(GUAC_LOG_INFO, "%i input file(s) provided.", total_files);
|
||||
|
||||
/* Interpret all input files */
|
||||
for (i = optind; i < argc; i++) {
|
||||
|
||||
/* Get current filename */
|
||||
const char* path = argv[i];
|
||||
|
||||
/* Generate output filename */
|
||||
char out_path[4096];
|
||||
int len = snprintf(out_path, sizeof(out_path), "%s.txt", path);
|
||||
|
||||
/* Do not write if filename exceeds maximum length */
|
||||
if (len >= sizeof(out_path)) {
|
||||
guaclog_log(GUAC_LOG_ERROR, "Cannot write output file for \"%s\": "
|
||||
"Name too long", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Attempt interpreting, log granular success/failure at debug level */
|
||||
if (guaclog_interpret(path, out_path, force)) {
|
||||
failures++;
|
||||
guaclog_log(GUAC_LOG_DEBUG,
|
||||
"%s was NOT successfully interpreted.", path);
|
||||
}
|
||||
else
|
||||
guaclog_log(GUAC_LOG_DEBUG, "%s was successfully "
|
||||
"interpreted.", path);
|
||||
|
||||
}
|
||||
|
||||
/* Warn if at least one file failed */
|
||||
if (failures != 0)
|
||||
guaclog_log(GUAC_LOG_WARNING, "Interpreting failed for %i of %i "
|
||||
"file(s).", failures, total_files);
|
||||
|
||||
/* Notify of success */
|
||||
else
|
||||
guaclog_log(GUAC_LOG_INFO, "All files interpreted successfully.");
|
||||
|
||||
/* Interpreting complete */
|
||||
return 0;
|
||||
|
||||
/* Display usage and exit with error if options are invalid */
|
||||
invalid_options:
|
||||
|
||||
fprintf(stderr, "USAGE: %s"
|
||||
" [-f]"
|
||||
" [FILE]...\n", argv[0]);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
31
src/guaclog/guaclog.h
Normal file
31
src/guaclog/guaclog.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 GUACLOG_H
|
||||
#define GUACLOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/**
|
||||
* The default log level below which no messages should be logged.
|
||||
*/
|
||||
#define GUACLOG_DEFAULT_LOG_LEVEL GUAC_LOG_INFO
|
||||
|
||||
#endif
|
||||
|
43
src/guaclog/instruction-key.c
Normal file
43
src/guaclog/instruction-key.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 "state.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int guaclog_handle_key(guaclog_state* state, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 2) {
|
||||
guaclog_log(GUAC_LOG_WARNING, "\"key\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int keysym = atoi(argv[0]);
|
||||
bool pressed = (atoi(argv[1]) != 0);
|
||||
|
||||
/* Update interpreter state accordingly */
|
||||
return guaclog_state_update_key(state, keysym, pressed);
|
||||
|
||||
}
|
||||
|
62
src/guaclog/instructions.c
Normal file
62
src/guaclog/instructions.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 "state.h"
|
||||
#include "instructions.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
guaclog_instruction_handler_mapping guaclog_instruction_handler_map[] = {
|
||||
{"key", guaclog_handle_key},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int guaclog_handle_instruction(guaclog_state* state, const char* opcode,
|
||||
int argc, char** argv) {
|
||||
|
||||
/* Search through mapping for instruction handler having given opcode */
|
||||
guaclog_instruction_handler_mapping* current = guaclog_instruction_handler_map;
|
||||
while (current->opcode != NULL) {
|
||||
|
||||
/* Invoke handler if opcode matches (if defined) */
|
||||
if (strcmp(current->opcode, opcode) == 0) {
|
||||
|
||||
/* Invoke defined handler */
|
||||
guaclog_instruction_handler* handler = current->handler;
|
||||
if (handler != NULL)
|
||||
return handler(state, argc, argv);
|
||||
|
||||
/* Log defined but unimplemented instructions */
|
||||
guaclog_log(GUAC_LOG_DEBUG, "\"%s\" not implemented", opcode);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Next candidate handler */
|
||||
current++;
|
||||
|
||||
} /* end opcode search */
|
||||
|
||||
/* Ignore any unknown instructions */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
108
src/guaclog/instructions.h
Normal file
108
src/guaclog/instructions.h
Normal file
@ -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.
|
||||
*/
|
||||
|
||||
#ifndef GUACLOG_INSTRUCTIONS_H
|
||||
#define GUACLOG_INSTRUCTIONS_H
|
||||
|
||||
#include "config.h"
|
||||
#include "state.h"
|
||||
|
||||
/**
|
||||
* A callback function which, when invoked, handles a particular Guacamole
|
||||
* instruction. The opcode of the instruction is implied (as it is expected
|
||||
* that there will be a 1:1 mapping of opcode to callback function), while the
|
||||
* arguments for that instruction are included in the parameters given to the
|
||||
* callback.
|
||||
*
|
||||
* @param state
|
||||
* The current state of the Guacamole input log interpreter.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments (excluding opcode) passed to the instruction
|
||||
* being handled by the callback.
|
||||
*
|
||||
* @param argv
|
||||
* All arguments (excluding opcode) associated with the instruction being
|
||||
* handled by the callback.
|
||||
*
|
||||
* @return
|
||||
* Zero if the instruction was handled successfully, non-zero if an error
|
||||
* occurs.
|
||||
*/
|
||||
typedef int guaclog_instruction_handler(guaclog_state* state,
|
||||
int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Mapping of instruction opcode to corresponding handler function.
|
||||
*/
|
||||
typedef struct guaclog_instruction_handler_mapping {
|
||||
|
||||
/**
|
||||
* The opcode of the instruction that the associated handler function
|
||||
* should be invoked for.
|
||||
*/
|
||||
const char* opcode;
|
||||
|
||||
/**
|
||||
* The handler function to invoke whenever an instruction having the
|
||||
* associated opcode is parsed.
|
||||
*/
|
||||
guaclog_instruction_handler* handler;
|
||||
|
||||
} guaclog_instruction_handler_mapping;
|
||||
|
||||
/**
|
||||
* Array of all opcode/handler mappings for all supported opcodes, terminated
|
||||
* by an entry with a NULL opcode. All opcodes not listed here can be safely
|
||||
* ignored.
|
||||
*/
|
||||
extern guaclog_instruction_handler_mapping guaclog_instruction_handler_map[];
|
||||
|
||||
/**
|
||||
* Handles the instruction having the given opcode and arguments, updating
|
||||
* the state of the interpreter accordingly.
|
||||
*
|
||||
* @param state
|
||||
* The current state of the Guacamole input log interpreter.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction being handled.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments (excluding opcode) passed to the instruction
|
||||
* being handled by the callback.
|
||||
*
|
||||
* @param argv
|
||||
* All arguments (excluding opcode) associated with the instruction being
|
||||
* handled by the callback.
|
||||
*
|
||||
* @return
|
||||
* Zero if the instruction was handled successfully, non-zero if an error
|
||||
* occurs.
|
||||
*/
|
||||
int guaclog_handle_instruction(guaclog_state* state,
|
||||
const char* opcode, int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "key" instruction.
|
||||
*/
|
||||
guaclog_instruction_handler guaclog_handle_key;
|
||||
|
||||
#endif
|
||||
|
152
src/guaclog/interpret.c
Normal file
152
src/guaclog/interpret.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 "instructions.h"
|
||||
#include "log.h"
|
||||
#include "state.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/parser.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Reads and handles all Guacamole instructions from the given guac_socket
|
||||
* until end-of-stream is reached.
|
||||
*
|
||||
* @param state
|
||||
* The current state of the Guacamole input log interpreter.
|
||||
*
|
||||
* @param path
|
||||
* The name of the file being parsed (for logging purposes). This file
|
||||
* must already be open and available through the given socket.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket through which instructions should be read.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero if parsing of Guacamole protocol data through
|
||||
* the given socket fails.
|
||||
*/
|
||||
static int guaclog_read_instructions(guaclog_state* state,
|
||||
const char* path, guac_socket* socket) {
|
||||
|
||||
/* Obtain Guacamole protocol parser */
|
||||
guac_parser* parser = guac_parser_alloc();
|
||||
if (parser == NULL)
|
||||
return 1;
|
||||
|
||||
/* Continuously read and handle all instructions */
|
||||
while (!guac_parser_read(parser, socket, -1)) {
|
||||
guaclog_handle_instruction(state, parser->opcode,
|
||||
parser->argc, parser->argv);
|
||||
}
|
||||
|
||||
/* Fail on read/parse error */
|
||||
if (guac_error != GUAC_STATUS_CLOSED) {
|
||||
guaclog_log(GUAC_LOG_ERROR, "%s: %s",
|
||||
path, guac_status_string(guac_error));
|
||||
guac_parser_free(parser);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse complete */
|
||||
guac_parser_free(parser);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guaclog_interpret(const char* path, const char* out_path, bool force) {
|
||||
|
||||
/* Open input file */
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
guaclog_log(GUAC_LOG_ERROR, "%s: %s", path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Lock entire input file for reading by the current process */
|
||||
struct flock file_lock = {
|
||||
.l_type = F_RDLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
.l_pid = getpid()
|
||||
};
|
||||
|
||||
/* Abort if file cannot be locked for reading */
|
||||
if (!force && fcntl(fd, F_SETLK, &file_lock) == -1) {
|
||||
|
||||
/* Warn if lock cannot be acquired */
|
||||
if (errno == EACCES || errno == EAGAIN)
|
||||
guaclog_log(GUAC_LOG_WARNING, "Refusing to interpret log of "
|
||||
"in-progress session \"%s\" (specify the -f option to "
|
||||
"override this behavior).", path);
|
||||
|
||||
/* Log an error if locking fails in an unexpected way */
|
||||
else
|
||||
guaclog_log(GUAC_LOG_ERROR, "Cannot lock \"%s\" for reading: %s",
|
||||
path, strerror(errno));
|
||||
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate input state for interpreting process */
|
||||
guaclog_state* state = guaclog_state_alloc(out_path);
|
||||
if (state == NULL) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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));
|
||||
close(fd);
|
||||
guaclog_state_free(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
guaclog_log(GUAC_LOG_INFO, "Writing input events from \"%s\" "
|
||||
"to \"%s\" ...", path, out_path);
|
||||
|
||||
/* Attempt to read all instructions in the file */
|
||||
if (guaclog_read_instructions(state, path, socket)) {
|
||||
guac_socket_free(socket);
|
||||
guaclog_state_free(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Close input and finish interpreting process */
|
||||
guac_socket_free(socket);
|
||||
return guaclog_state_free(state);
|
||||
|
||||
}
|
||||
|
51
src/guaclog/interpret.h
Normal file
51
src/guaclog/interpret.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 GUACLOG_INTERPRET_H
|
||||
#define GUACLOG_INTERPRET_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Interprets all input events within the given Guacamole protocol dump,
|
||||
* producing a human-readable log of those input events. A read lock will be
|
||||
* acquired on the input file to ensure that in-progress logs are not
|
||||
* interpreted. This behavior can be overridden by specifying true for the
|
||||
* force parameter.
|
||||
*
|
||||
* @param path
|
||||
* The path to the file containing the raw Guacamole protocol dump.
|
||||
*
|
||||
* @param out_path
|
||||
* The full path to the file in which interpreted log should be written.
|
||||
*
|
||||
* @param force
|
||||
* Interpret even if the input file appears to be an in-progress log (has
|
||||
* an associated lock).
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero if an error prevented successful
|
||||
* interpretation of the log.
|
||||
*/
|
||||
int guaclog_interpret(const char* path, const char* out_path, bool force);
|
||||
|
||||
#endif
|
||||
|
335
src/guaclog/keydef.c
Normal file
335
src/guaclog/keydef.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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 "keydef.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* All known keys.
|
||||
*/
|
||||
const guaclog_keydef known_keys[] = {
|
||||
{ 0xFE03, "AltGr", "", true },
|
||||
{ 0xFF08, "Backspace" },
|
||||
{ 0xFF09, "Tab" },
|
||||
{ 0xFF0B, "Clear" },
|
||||
{ 0xFF0D, "Return", "\n" },
|
||||
{ 0xFF13, "Pause" },
|
||||
{ 0xFF14, "Scroll" },
|
||||
{ 0xFF15, "SysReq" },
|
||||
{ 0xFF1B, "Escape" },
|
||||
{ 0xFF50, "Home" },
|
||||
{ 0xFF51, "Left" },
|
||||
{ 0xFF52, "Up" },
|
||||
{ 0xFF53, "Right" },
|
||||
{ 0xFF54, "Down" },
|
||||
{ 0xFF55, "Page Up" },
|
||||
{ 0xFF56, "Page Down" },
|
||||
{ 0xFF57, "End" },
|
||||
{ 0xFF63, "Insert" },
|
||||
{ 0xFF65, "Undo" },
|
||||
{ 0xFF6A, "Help" },
|
||||
{ 0xFF7F, "Num" },
|
||||
{ 0xFF80, "Space", " " },
|
||||
{ 0xFF8D, "Enter", "\n" },
|
||||
{ 0xFF95, "Home" },
|
||||
{ 0xFF96, "Left" },
|
||||
{ 0xFF97, "Up" },
|
||||
{ 0xFF98, "Right" },
|
||||
{ 0xFF99, "Down" },
|
||||
{ 0xFF9A, "Page Up" },
|
||||
{ 0xFF9B, "Page Down" },
|
||||
{ 0xFF9C, "End" },
|
||||
{ 0xFF9E, "Insert" },
|
||||
{ 0xFFAA, "*", "*" },
|
||||
{ 0xFFAB, "+", "+" },
|
||||
{ 0xFFAD, "-", "-" },
|
||||
{ 0xFFAE, ".", "." },
|
||||
{ 0xFFAF, "/", "/" },
|
||||
{ 0xFFB0, "0", "0" },
|
||||
{ 0xFFB1, "1", "1" },
|
||||
{ 0xFFB2, "2", "2" },
|
||||
{ 0xFFB3, "3", "3" },
|
||||
{ 0xFFB4, "4", "4" },
|
||||
{ 0xFFB5, "5", "5" },
|
||||
{ 0xFFB6, "6", "6" },
|
||||
{ 0xFFB7, "7", "7" },
|
||||
{ 0xFFB8, "8", "8" },
|
||||
{ 0xFFB9, "9", "9" },
|
||||
{ 0xFFBE, "F1" },
|
||||
{ 0xFFBF, "F2" },
|
||||
{ 0xFFC0, "F3" },
|
||||
{ 0xFFC1, "F4" },
|
||||
{ 0xFFC2, "F5" },
|
||||
{ 0xFFC3, "F6" },
|
||||
{ 0xFFC4, "F7" },
|
||||
{ 0xFFC5, "F8" },
|
||||
{ 0xFFC6, "F9" },
|
||||
{ 0xFFC7, "F10" },
|
||||
{ 0xFFC8, "F11" },
|
||||
{ 0xFFC9, "F12" },
|
||||
{ 0xFFCA, "F13" },
|
||||
{ 0xFFCB, "F14" },
|
||||
{ 0xFFCC, "F15" },
|
||||
{ 0xFFCD, "F16" },
|
||||
{ 0xFFCE, "F17" },
|
||||
{ 0xFFCF, "F18" },
|
||||
{ 0xFFD0, "F19" },
|
||||
{ 0xFFD1, "F20" },
|
||||
{ 0xFFD2, "F21" },
|
||||
{ 0xFFD3, "F22" },
|
||||
{ 0xFFD4, "F23" },
|
||||
{ 0xFFD5, "F24" },
|
||||
{ 0xFFE1, "Shift", "", true },
|
||||
{ 0xFFE2, "Shift", "", true },
|
||||
{ 0xFFE3, "Ctrl", NULL, true },
|
||||
{ 0xFFE4, "Ctrl", NULL, true },
|
||||
{ 0xFFE5, "Caps" },
|
||||
{ 0xFFE7, "Meta", NULL, true },
|
||||
{ 0xFFE8, "Meta", NULL, true },
|
||||
{ 0xFFE9, "Alt", NULL, true },
|
||||
{ 0xFFEA, "Alt", NULL, true },
|
||||
{ 0xFFEB, "Super", NULL, true },
|
||||
{ 0xFFEC, "Super", NULL, true },
|
||||
{ 0xFFED, "Hyper", NULL, true },
|
||||
{ 0xFFEE, "Hyper", NULL, true },
|
||||
{ 0xFFFF, "Delete" }
|
||||
};
|
||||
|
||||
/**
|
||||
* Comparator for the standard bsearch() function which compares an integer
|
||||
* keysym against the keysym associated with a guaclog_keydef.
|
||||
*
|
||||
* @param key
|
||||
* The key value being compared against the member. This MUST be the
|
||||
* keysym value, passed through typecasting to an intptr_t (NOT a pointer
|
||||
* to the int itself).
|
||||
*
|
||||
* @param member
|
||||
* The member within the known_keys array being compared against the given
|
||||
* key.
|
||||
*
|
||||
* @return
|
||||
* Zero if the given keysym is equal to that of the given member, a
|
||||
* positive value if the given keysym is greater than that of the given
|
||||
* member, or a negative value if the given keysym is less than that of the
|
||||
* given member.
|
||||
*/
|
||||
static int guaclog_keydef_bsearch_compare(const void* key,
|
||||
const void* member) {
|
||||
|
||||
int keysym = (int) ((intptr_t) key);
|
||||
guaclog_keydef* current = (guaclog_keydef*) member;
|
||||
|
||||
/* Compare given keysym to keysym of current member */
|
||||
return keysym - current->keysym;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches through the known_keys array of known keys for the name of the key
|
||||
* having the given keysym, returning a pointer to the static guaclog_keydef
|
||||
* within the array if found.
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key.
|
||||
*
|
||||
* @return
|
||||
* A pointer to the static guaclog_keydef associated with the given keysym,
|
||||
* or NULL if the key could not be found.
|
||||
*/
|
||||
static guaclog_keydef* guaclog_get_known_key(int keysym) {
|
||||
|
||||
/* Search through known keys for given keysym */
|
||||
return bsearch((void*) ((intptr_t) keysym),
|
||||
known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
|
||||
sizeof(known_keys[0]), guaclog_keydef_bsearch_compare);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a statically-allocated guaclog_keydef representing an unknown key,
|
||||
* deriving the name of the key from the hexadecimal value of the keysym.
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key.
|
||||
*
|
||||
* @return
|
||||
* A statically-allocated guaclog_keydef representing the key associated
|
||||
* with the given keysym.
|
||||
*/
|
||||
static guaclog_keydef* guaclog_get_unknown_key(int keysym) {
|
||||
|
||||
static char unknown_keydef_name[64];
|
||||
static guaclog_keydef unknown_keydef;
|
||||
|
||||
/* Write keysym as hex */
|
||||
int size = snprintf(unknown_keydef_name, sizeof(unknown_keydef_name),
|
||||
"0x%X", keysym);
|
||||
|
||||
/* Hex string is guaranteed to fit within the provided 64 bytes */
|
||||
assert(size < sizeof(unknown_keydef_name));
|
||||
|
||||
/* Return static key definition */
|
||||
unknown_keydef.keysym = keysym;
|
||||
unknown_keydef.name = unknown_keydef_name;
|
||||
return &unknown_keydef;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a statically-allocated guaclog_keydef representing the key
|
||||
* associated with the given keysym, deriving the name and value of the key
|
||||
* using its corresponding Unicode character.
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key.
|
||||
*
|
||||
* @return
|
||||
* A statically-allocated guaclog_keydef representing the key associated
|
||||
* with the given keysym, or NULL if the given keysym has no corresponding
|
||||
* Unicode character.
|
||||
*/
|
||||
static guaclog_keydef* guaclog_get_unicode_key(int keysym) {
|
||||
|
||||
static char unicode_keydef_name[8];
|
||||
|
||||
static guaclog_keydef unicode_keydef;
|
||||
|
||||
int i;
|
||||
int mask, bytes;
|
||||
|
||||
/* Translate only if keysym maps to Unicode */
|
||||
if (keysym < 0x00 || (keysym > 0xFF && (keysym & 0xFFFF0000) != 0x01000000))
|
||||
return NULL;
|
||||
|
||||
int codepoint = keysym & 0xFFFF;
|
||||
|
||||
/* Determine size and initial byte mask */
|
||||
if (codepoint <= 0x007F) {
|
||||
mask = 0x00;
|
||||
bytes = 1;
|
||||
}
|
||||
else if (codepoint <= 0x7FF) {
|
||||
mask = 0xC0;
|
||||
bytes = 2;
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
mask = 0xE0;
|
||||
bytes = 3;
|
||||
}
|
||||
else if (codepoint <= 0x1FFFFF) {
|
||||
mask = 0xF0;
|
||||
bytes = 4;
|
||||
}
|
||||
|
||||
/* Otherwise, invalid codepoint */
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* Offset buffer by size */
|
||||
char* key_name = unicode_keydef_name + bytes;
|
||||
|
||||
/* Add null terminator */
|
||||
*(key_name--) = '\0';
|
||||
|
||||
/* Add trailing bytes, if any */
|
||||
for (i=1; i<bytes; i++) {
|
||||
*(key_name--) = 0x80 | (codepoint & 0x3F);
|
||||
codepoint >>= 6;
|
||||
}
|
||||
|
||||
/* Set initial byte */
|
||||
*key_name = mask | codepoint;
|
||||
|
||||
/* Return static key definition */
|
||||
unicode_keydef.keysym = keysym;
|
||||
unicode_keydef.name = unicode_keydef.value = unicode_keydef_name;
|
||||
return &unicode_keydef;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given guaclog_keydef into a newly-allocated guaclog_keydef
|
||||
* structure. The resulting guaclog_keydef must eventually be freed through a
|
||||
* call to guaclog_keydef_free().
|
||||
*
|
||||
* @param keydef
|
||||
* The guaclog_keydef to copy.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated guaclog_keydef structure copied from the given
|
||||
* guaclog_keydef.
|
||||
*/
|
||||
static guaclog_keydef* guaclog_copy_key(guaclog_keydef* keydef) {
|
||||
|
||||
guaclog_keydef* copy = malloc(sizeof(guaclog_keydef));
|
||||
|
||||
/* Always copy keysym and name */
|
||||
copy->keysym = keydef->keysym;
|
||||
copy->name = strdup(keydef->name);
|
||||
|
||||
/* Copy value only if defined */
|
||||
if (keydef->value != NULL)
|
||||
copy->value = strdup(keydef->value);
|
||||
else
|
||||
copy->value = NULL;
|
||||
|
||||
return copy;
|
||||
|
||||
}
|
||||
|
||||
guaclog_keydef* guaclog_keydef_alloc(int keysym) {
|
||||
|
||||
guaclog_keydef* keydef;
|
||||
|
||||
/* Check list of known keys first */
|
||||
keydef = guaclog_get_known_key(keysym);
|
||||
if (keydef != NULL)
|
||||
return guaclog_copy_key(keydef);
|
||||
|
||||
/* Failing that, attempt to translate straight into a Unicode character */
|
||||
keydef = guaclog_get_unicode_key(keysym);
|
||||
if (keydef != NULL)
|
||||
return guaclog_copy_key(keydef);
|
||||
|
||||
/* Key not known */
|
||||
guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
|
||||
return guaclog_copy_key(guaclog_get_unknown_key(keysym));
|
||||
|
||||
}
|
||||
|
||||
void guaclog_keydef_free(guaclog_keydef* keydef) {
|
||||
|
||||
/* Ignore NULL keydef */
|
||||
if (keydef == NULL)
|
||||
return;
|
||||
|
||||
free(keydef->name);
|
||||
free(keydef->value);
|
||||
free(keydef);
|
||||
|
||||
}
|
||||
|
81
src/guaclog/keydef.h
Normal file
81
src/guaclog/keydef.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 GUACLOG_KEYDEF_H
|
||||
#define GUACLOG_KEYDEF_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* A mapping of X11 keysym to its corresponding human-readable name.
|
||||
*/
|
||||
typedef struct guaclog_keydef {
|
||||
|
||||
/**
|
||||
* The X11 keysym of the key.
|
||||
*/
|
||||
int keysym;
|
||||
|
||||
/**
|
||||
* A human-readable name for the key.
|
||||
*/
|
||||
char* name;
|
||||
|
||||
/**
|
||||
* The value which would be typed in a typical text editor, if any. If the
|
||||
* key is not associated with any typable value, or if the typable value is
|
||||
* not generally useful in an auditing context, this will be NULL.
|
||||
*/
|
||||
char* value;
|
||||
|
||||
/**
|
||||
* Whether this key is a modifier which may affect the interpretation of
|
||||
* other keys, and thus should be tracked as it is held down.
|
||||
*/
|
||||
bool modifier;
|
||||
|
||||
} guaclog_keydef;
|
||||
|
||||
/**
|
||||
* Creates a new guaclog_keydef which represents the key having the given
|
||||
* keysym. The resulting guaclog_keydef must eventually be freed through a
|
||||
* call to guaclog_keydef_free().
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key.
|
||||
*
|
||||
* @return
|
||||
* A new guaclog_keydef which represents the key having the given keysym,
|
||||
* or NULL if no such key is known.
|
||||
*/
|
||||
guaclog_keydef* guaclog_keydef_alloc(int keysym);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the given guaclog_keydef. If the given
|
||||
* guaclog_keydef is NULL, this function has no effect.
|
||||
*
|
||||
* @param keydef
|
||||
* The guaclog_keydef to free, which may be NULL.
|
||||
*/
|
||||
void guaclog_keydef_free(guaclog_keydef* keydef);
|
||||
|
||||
#endif
|
||||
|
85
src/guaclog/log.c
Normal file
85
src/guaclog/log.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "guaclog.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/error.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int guaclog_log_level = GUACLOG_DEFAULT_LOG_LEVEL;
|
||||
|
||||
void vguaclog_log(guac_client_log_level level, const char* format,
|
||||
va_list args) {
|
||||
|
||||
const char* priority_name;
|
||||
char message[2048];
|
||||
|
||||
/* Don't bother if the log level is too high */
|
||||
if (level > guaclog_log_level)
|
||||
return;
|
||||
|
||||
/* Copy log message into buffer */
|
||||
vsnprintf(message, sizeof(message), format, args);
|
||||
|
||||
/* Convert log level to human-readable name */
|
||||
switch (level) {
|
||||
|
||||
/* Error log level */
|
||||
case GUAC_LOG_ERROR:
|
||||
priority_name = "ERROR";
|
||||
break;
|
||||
|
||||
/* Warning log level */
|
||||
case GUAC_LOG_WARNING:
|
||||
priority_name = "WARNING";
|
||||
break;
|
||||
|
||||
/* Informational log level */
|
||||
case GUAC_LOG_INFO:
|
||||
priority_name = "INFO";
|
||||
break;
|
||||
|
||||
/* Debug log level */
|
||||
case GUAC_LOG_DEBUG:
|
||||
priority_name = "DEBUG";
|
||||
break;
|
||||
|
||||
/* Any unknown/undefined log level */
|
||||
default:
|
||||
priority_name = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Log to STDERR */
|
||||
fprintf(stderr, GUACLOG_LOG_NAME ": %s: %s\n", priority_name, message);
|
||||
|
||||
}
|
||||
|
||||
void guaclog_log(guac_client_log_level level, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vguaclog_log(level, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
73
src/guaclog/log.h
Normal file
73
src/guaclog/log.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 GUACLOG_LOG_H
|
||||
#define GUACLOG_LOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* The maximum level at which to log messages. All other messages will be
|
||||
* dropped.
|
||||
*/
|
||||
extern int guaclog_log_level;
|
||||
|
||||
/**
|
||||
* The string to prepend to all log messages.
|
||||
*/
|
||||
#define GUACLOG_LOG_NAME "guaclog"
|
||||
|
||||
/**
|
||||
* Writes a message to guaclog's logs. This function takes a format and
|
||||
* va_list, similar to vprintf.
|
||||
*
|
||||
* @param level
|
||||
* The level at which to log this message.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string to log.
|
||||
*
|
||||
* @param args
|
||||
* The va_list containing the arguments to be used when filling the format
|
||||
* string for printing.
|
||||
*/
|
||||
void vguaclog_log(guac_client_log_level level, const char* format,
|
||||
va_list args);
|
||||
|
||||
/**
|
||||
* Writes a message to guaclog's logs. This function accepts parameters
|
||||
* identically to printf.
|
||||
*
|
||||
* @param level
|
||||
* The level at which to log this message.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string to log.
|
||||
*
|
||||
* @param ...
|
||||
* Arguments to use when filling the format string for printing.
|
||||
*/
|
||||
void guaclog_log(guac_client_log_level level, const char* format, ...);
|
||||
|
||||
#endif
|
||||
|
99
src/guaclog/man/guaclog.1.in
Normal file
99
src/guaclog/man/guaclog.1.in
Normal file
@ -0,0 +1,99 @@
|
||||
.\"
|
||||
.\" 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.
|
||||
.\"
|
||||
.TH guaclog 1 "26 Jan 2018" "version @PACKAGE_VERSION@" "Apache Guacamole"
|
||||
.
|
||||
.SH NAME
|
||||
guaclog \- Guacamole input log interpreter
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B guaclog
|
||||
[\fB-f\fR]
|
||||
[\fIFILE\fR]...
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B guaclog
|
||||
is an interpreter which accepts Guacamole protocol dumps, such as those saved
|
||||
when input logging is enabled for a Guacamole session recording, writing
|
||||
human-readable text logs as output.
|
||||
.B guaclog
|
||||
is essentially an implementation of a Guacamole client which accepts
|
||||
its input from files instead of a network connection, however unlike
|
||||
.B guacenc
|
||||
it only handles instructions related to user input.
|
||||
.P
|
||||
Each \fIFILE\fR specified will be translated into a new human-readable text
|
||||
file named \fIFILE\fR.txt. Existing files will not be overwritten; the
|
||||
interpreting process for any input file will be aborted if it would result in
|
||||
overwriting an existing file.
|
||||
.P
|
||||
Guacamole acquires a write lock on recordings as they are being written. By
|
||||
default,
|
||||
.B guaclog
|
||||
will check whether the each input file is locked and will refuse to read and
|
||||
interpret an input file if it appears to be an in-progress recording. This
|
||||
behavior can be overridden by specifying the \fB-f\fR option. Interpreting an
|
||||
in-progress recording will still work; the resulting human-readable text file
|
||||
will simply cover the user's session only up to the current point in time.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-f\fR
|
||||
Overrides the default behavior of
|
||||
.B guaclog
|
||||
such that input files will be interpreted even if they appear to be recordings
|
||||
of in-progress Guacamole sessions.
|
||||
.
|
||||
.SH OUTPUT FORMAT
|
||||
The output format of
|
||||
.B guaclog
|
||||
is meant to match what the user would have typed within a typical text editor
|
||||
as closely as possible, while also representing non-printable characters and
|
||||
keyboard shortcuts in a human-readable way.
|
||||
.P
|
||||
All output is on one line, with new lines started only as a result of the user
|
||||
pressing enter/return. Keys which produce printable characters are translated
|
||||
into their corresponding Unicode codepoints and encoded as UTF-8, while
|
||||
non-printable characters are enclosed within angle brackets and represented
|
||||
with their human-readable names. Keyboard shortcuts which are made up of more
|
||||
than one key are enclosed within angle brackets, with each key within the
|
||||
shortcut separated by plus signs.
|
||||
.P
|
||||
Spaces and newlines are included as their Unicode character, except when
|
||||
represented within a keyboard shortcut, in which case their human-readable
|
||||
names are used instead. As the output of pressing tab can be easily mistaken
|
||||
for spaces, and as pressing tab frequently has special meaning within
|
||||
applications, tab is always represented by its human-readable name.
|
||||
.P
|
||||
Modifiers are output as part of keyboard shortcuts only. Simple pressing and
|
||||
releasing of a modifier will be ignored, as are presses of shift or AltGr while
|
||||
typing.
|
||||
.P
|
||||
For example, if the user typed "Hello WORLD!", selected everything by pressing
|
||||
Ctrl+a, copied the selected text by pressing Ctrl+c, switched to another
|
||||
application by pressing Alt+Shift+Tab, and then pasted the previously-copied
|
||||
text by pressing Ctrl+v, the resulting log from
|
||||
.B
|
||||
guaclog
|
||||
would look like:
|
||||
.PP
|
||||
.RS 0
|
||||
Hello WORLD!<Ctrl+a><Ctrl+c><Alt+Shift+Tab><Ctrl+v>
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR guacenc (1)
|
266
src/guaclog/state.c
Normal file
266
src/guaclog/state.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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 "keydef.h"
|
||||
#include "log.h"
|
||||
#include "state.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
guaclog_state* guaclog_state_alloc(const char* path) {
|
||||
|
||||
/* Open output file */
|
||||
int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
guaclog_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
|
||||
path, strerror(errno));
|
||||
goto fail_output_fd;
|
||||
}
|
||||
|
||||
/* Create stream for output file */
|
||||
FILE* output = fdopen(fd, "wb");
|
||||
if (output == NULL) {
|
||||
guaclog_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
|
||||
"file \"%s\": %s", path, strerror(errno));
|
||||
goto fail_output_file;
|
||||
}
|
||||
|
||||
/* Allocate state */
|
||||
guaclog_state* state = (guaclog_state*) calloc(1, sizeof(guaclog_state));
|
||||
if (state == NULL) {
|
||||
goto fail_state;
|
||||
}
|
||||
|
||||
/* Associate state with output file */
|
||||
state->output = output;
|
||||
|
||||
/* No keys are initially tracked */
|
||||
state->active_keys = 0;
|
||||
|
||||
return state;
|
||||
|
||||
/* Free all allocated data in case of failure */
|
||||
fail_state:
|
||||
fclose(output);
|
||||
|
||||
fail_output_file:
|
||||
close(fd);
|
||||
|
||||
fail_output_fd:
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int guaclog_state_free(guaclog_state* state) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Ignore NULL state */
|
||||
if (state == NULL)
|
||||
return 0;
|
||||
|
||||
/* Free keydefs of all tracked keys */
|
||||
for (i = 0; i < state->active_keys; i++)
|
||||
guaclog_keydef_free(state->key_states[i].keydef);
|
||||
|
||||
/* Close output file */
|
||||
fclose(state->output);
|
||||
|
||||
free(state);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given key state to the array of tracked keys. If the key is already
|
||||
* being tracked, its corresponding entry within the array of tracked keys is
|
||||
* updated, and the number of tracked keys remains the same. If the key is not
|
||||
* already being tracked, it is added to the end of the array of tracked keys
|
||||
* providing there is space available, and the number of tracked keys is
|
||||
* updated. Failures to add keys will be automatically logged.
|
||||
*
|
||||
* @param state
|
||||
* The Guacamole input log interpreter state being updated.
|
||||
*
|
||||
* @param keydef
|
||||
* The guaclog_keydef of the key being pressed or released. This
|
||||
* guaclog_keydef will automatically be freed along with the guaclog_state
|
||||
* if the key state was successfully added, and must be manually freed
|
||||
* otherwise.
|
||||
*
|
||||
* @param pressed
|
||||
* true if the key is being pressed, false if the key is being released.
|
||||
*
|
||||
* @return
|
||||
* Zero if the key state was successfully added, non-zero otherwise.
|
||||
*/
|
||||
static int guaclog_state_add_key(guaclog_state* state, guaclog_keydef* keydef,
|
||||
bool pressed) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Update existing key, if already tracked */
|
||||
for (i = 0; i < state->active_keys; i++) {
|
||||
guaclog_key_state* key = &state->key_states[i];
|
||||
if (key->keydef->keysym == keydef->keysym) {
|
||||
guaclog_keydef_free(key->keydef);
|
||||
key->keydef = keydef;
|
||||
key->pressed = pressed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If not already tracked, we need space to add it */
|
||||
if (state->active_keys == GUACLOG_MAX_KEYS) {
|
||||
guaclog_log(GUAC_LOG_WARNING, "Unable to log key 0x%X: Too many "
|
||||
"active keys.", keydef->keysym);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Add key to state */
|
||||
guaclog_key_state* key = &state->key_states[state->active_keys++];
|
||||
key->keydef = keydef;
|
||||
key->pressed = pressed;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes released keys from the end of the array of tracked keys, such that
|
||||
* the last key in the array is a pressed key. This function should be invoked
|
||||
* after changes have been made to the interpreter state, to ensure that the
|
||||
* array of tracked keys does not grow longer than necessary.
|
||||
*
|
||||
* @param state
|
||||
* The Guacamole input log interpreter state to trim.
|
||||
*/
|
||||
static void guaclog_state_trim_keys(guaclog_state* state) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Reset active_keys to contain only up to the last pressed key */
|
||||
for (i = state->active_keys - 1; i >= 0; i--) {
|
||||
|
||||
guaclog_key_state* key = &state->key_states[i];
|
||||
if (key->pressed) {
|
||||
state->active_keys = i + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free all trimmed states */
|
||||
guaclog_keydef_free(key->keydef);
|
||||
|
||||
}
|
||||
|
||||
/* No keys are active */
|
||||
state->active_keys = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current tracked key state represents an in-progress
|
||||
* keyboard shortcut.
|
||||
*
|
||||
* @param state
|
||||
* The Guacamole input log interpreter state to test.
|
||||
*
|
||||
* @return
|
||||
* true if the given state represents an in-progress keyboard shortcut,
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool guaclog_state_is_shortcut(guaclog_state* state) {
|
||||
|
||||
int i;
|
||||
|
||||
/* We are in a shortcut if at least one key is non-printable */
|
||||
for (i = 0; i < state->active_keys; i++) {
|
||||
guaclog_key_state* key = &state->key_states[i];
|
||||
if (key->keydef->value == NULL)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* All keys are printable - no shortcut */
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Determine nature of key */
|
||||
guaclog_keydef* keydef = guaclog_keydef_alloc(keysym);
|
||||
if (keydef == NULL)
|
||||
return 0;
|
||||
|
||||
/* Update tracked key state for modifiers */
|
||||
if (keydef->modifier) {
|
||||
if (guaclog_state_add_key(state, keydef, pressed))
|
||||
guaclog_keydef_free(keydef);
|
||||
else
|
||||
guaclog_state_trim_keys(state);
|
||||
}
|
||||
|
||||
/* Output key states only for printable keys */
|
||||
else if (pressed) {
|
||||
|
||||
if (guaclog_state_is_shortcut(state)) {
|
||||
|
||||
fprintf(state->output, "<");
|
||||
|
||||
/* Compose log entry by inspecting the state of each tracked key */
|
||||
for (i = 0; i < state->active_keys; i++) {
|
||||
|
||||
/* Translate keysym into human-readable name */
|
||||
guaclog_key_state* key = &state->key_states[i];
|
||||
|
||||
/* Print name of key */
|
||||
if (i == 0)
|
||||
fprintf(state->output, "%s", key->keydef->name);
|
||||
else
|
||||
fprintf(state->output, "+%s", key->keydef->name);
|
||||
|
||||
}
|
||||
|
||||
fprintf(state->output, "%s>", keydef->value);
|
||||
|
||||
}
|
||||
|
||||
/* Print the key itself */
|
||||
else {
|
||||
if (keydef->value != NULL)
|
||||
fprintf(state->output, "%s", keydef->value);
|
||||
else
|
||||
fprintf(state->output, "<%s>", keydef->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
125
src/guaclog/state.h
Normal file
125
src/guaclog/state.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 GUACLOG_STATE_H
|
||||
#define GUACLOG_STATE_H
|
||||
|
||||
#include "config.h"
|
||||
#include "keydef.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* The maximum number of keys which may be tracked at any one time before
|
||||
* newly-pressed keys are ignored.
|
||||
*/
|
||||
#define GUACLOG_MAX_KEYS 256
|
||||
|
||||
/**
|
||||
* The current state of a single key.
|
||||
*/
|
||||
typedef struct guaclog_key_state {
|
||||
|
||||
/**
|
||||
* The definition of the key.
|
||||
*/
|
||||
guaclog_keydef* keydef;
|
||||
|
||||
/**
|
||||
* Whether the key is currently pressed (true) or released (false).
|
||||
*/
|
||||
bool pressed;
|
||||
|
||||
} guaclog_key_state;
|
||||
|
||||
/**
|
||||
* The current state of the Guacamole input log interpreter.
|
||||
*/
|
||||
typedef struct guaclog_state {
|
||||
|
||||
/**
|
||||
* Output file stream.
|
||||
*/
|
||||
FILE* output;
|
||||
|
||||
/**
|
||||
* The number of keys currently being tracked within the key_states array.
|
||||
*/
|
||||
int active_keys;
|
||||
|
||||
/**
|
||||
* Array of all keys currently being tracked. A key is added to the array
|
||||
* when it is pressed for the first time. Released keys at the end of the
|
||||
* array are automatically removed from tracking.
|
||||
*/
|
||||
guaclog_key_state key_states[GUACLOG_MAX_KEYS];
|
||||
|
||||
} guaclog_state;
|
||||
|
||||
/**
|
||||
* Allocates a new state structure for the Guacamole input log interpreter.
|
||||
* This structure serves as the representation of interpreter state as
|
||||
* input-related instructions are read and handled.
|
||||
*
|
||||
* @param path
|
||||
* The full path to the file in which interpreted, human-readable should be
|
||||
* written.
|
||||
*
|
||||
* @return
|
||||
* The newly-allocated Guacamole input log interpreter state, or NULL if
|
||||
* the state could not be allocated.
|
||||
*/
|
||||
guaclog_state* guaclog_state_alloc(const char* path);
|
||||
|
||||
/**
|
||||
* Frees all memory associated with the given Guacamole input log interpreter
|
||||
* state, and finishes any remaining interpreting process. If the given state
|
||||
* is NULL, this function has no effect.
|
||||
*
|
||||
* @param state
|
||||
* The Guacamole input log interpreter state to free, which may be NULL.
|
||||
*
|
||||
* @return
|
||||
* Zero if the interpreting process completed successfully, non-zero
|
||||
* otherwise.
|
||||
*/
|
||||
int guaclog_state_free(guaclog_state* state);
|
||||
|
||||
/**
|
||||
* Updates the given Guacamole input log interpreter state, marking the given
|
||||
* key as pressed or released.
|
||||
*
|
||||
* @param state
|
||||
* The Guacamole input log interpreter state being updated.
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key being pressed or released.
|
||||
*
|
||||
* @param pressed
|
||||
* true if the key is being pressed, false if the key is being released.
|
||||
*
|
||||
* @return
|
||||
* Zero if the interpreter state was updated successfully, non-zero
|
||||
* otherwise.
|
||||
*/
|
||||
int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user