diff --git a/.cirrus.yml b/.cirrus.yml index 30bf85deb1..3c313938a6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,9 +4,9 @@ FreeBSD_task: SSL: libressl matrix: freebsd_instance: - image_family: freebsd-13-3 + image_family: freebsd-14-2 prepare_script: - - pkg install -y $SSL git autoconf automake libtool pkgconf opus jpeg-turbo fdk-aac pixman libX11 libXfixes libXrandr libxkbfile nasm fusefs-libs check imlib2 freetype2 cmocka ibus + - pkg install -y $SSL git autoconf automake libtool pkgconf opus jpeg-turbo fdk-aac pixman libX11 libXfixes libXrandr libxkbfile nasm fusefs-libs3 check imlib2 freetype2 cmocka ibus - git submodule update --init --recursive configure_script: - ./bootstrap diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3b54534858..d180037ce8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -6,8 +6,11 @@ body: - type: markdown attributes: value: | - Before submitting a bug, read the [FAQ](https://github.com/neutrinolabs/xrdp/wiki/Tips-and-FAQ). - **In particular, on systemd-based systems. make sure you are not logged in on the console as the same user you are trying to use for xrdp** + Before submitting a bug, read the [FAQ](https://github.com/neutrinolabs/xrdp/wiki/Tips-and-FAQ). **In particular, on systemd-based systems. make sure you are not logged in on the console as the same user you are trying to use for xrdp** + + Please do not include links to images or videos on external websites. These are not guaranteed to always be available, and could be used to compromise web browsers. + + Videos hosted on github have a size limit (currently 10MB). If your video is larger than this, please upload it to https://youtube.com - type: input attributes: label: xrdp version diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60959ad5ac..33754d0340 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -132,10 +132,11 @@ jobs: CONF_FLAGS_amd64_max: "--enable-ibus --enable-ipv6 --enable-jpeg --enable-fuse --enable-mp3lame --enable-fdkaac --enable-opus --enable-rfxcodec --enable-painter --enable-pixman --enable-utmp - --with-imlib2 --with-freetype2 --enable-tests --with-x264" - CONF_FLAGS_i386_max: "--enable-ibus --enable-ipv6 --enable-jpeg + --with-imlib2 --with-freetype2 --enable-tests --with-x264 + --enable-openh264" + CONF_FLAGS_i386_max: "--enable-ipv6 --enable-jpeg --enable-mp3lame --enable-opus --enable-rfxcodec - --enable-painter --disable-pixman --with-imlib2 + --enable-painter --disable-pixman --with-freetype2 --host=i686-linux --enable-tests" PKG_CONFIG_PATH_i386: "/usr/lib/i386-linux-gnu/pkgconfig" @@ -167,7 +168,7 @@ jobs: if: ${{ matrix.DISTCHECK }} run: make distcheck -j $(nproc) - name: "Artifact: test-suite.log distcheck" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() && steps.dist_check.outcome == 'failure' with: name: test-suite-distcheck-${{ matrix.cc }}-${{ matrix.feature_set }} @@ -180,7 +181,7 @@ jobs: CC: gcc # This is required to use a version of cppcheck other than that # supplied with the operating system - CPPCHECK_VER: "2.15.0" + CPPCHECK_VER: "2.16.0" CPPCHECK_REPO: https://github.com/danmar/cppcheck.git steps: # Set steps.os.outputs.image to the specific OS (e.g. 'ubuntu20') diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 517d31c3cb..156e4c7466 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -15,6 +15,7 @@ jobs: CONF_FLAGS_amd64_max: "--enable-ipv6 --enable-jpeg --enable-fuse --enable-mp3lame --enable-fdkaac --enable-opus --enable-rfxcodec --enable-painter --enable-pixman --enable-utmp + --enable-x264 --enable-openh264 --with-imlib2 --with-freetype2 --enable-tests" steps: - uses: actions/checkout@v4 diff --git a/common/Makefile.am b/common/Makefile.am index 3619a55d3d..9e8b6625d6 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -24,7 +24,7 @@ include_HEADERS = \ xrdp_sockets.h AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/common/ms-erref.h b/common/ms-erref.h index 394766a1ad..47a8399b33 100644 --- a/common/ms-erref.h +++ b/common/ms-erref.h @@ -32,6 +32,7 @@ enum NTSTATUS STATUS_NO_MORE_FILES = 0x80000006, STATUS_UNSUCCESSFUL = 0xc0000001, + STATUS_INFO_LENGTH_MISMATCH = 0xc0000004, STATUS_NO_SUCH_FILE = 0xc000000f, STATUS_ACCESS_DENIED = 0xc0000022, STATUS_OBJECT_NAME_INVALID = 0xc0000033, diff --git a/common/ms-fscc.h b/common/ms-fscc.h index 2f8cc20174..781d646370 100644 --- a/common/ms-fscc.h +++ b/common/ms-fscc.h @@ -30,7 +30,7 @@ /* * File information classes (section 2.4) */ -enum FS_INFORMATION_CLASS +enum FILE_INFORMATION_CLASS { FileAllocationInformation = 19, /* Set */ FileBasicInformation = 4, /* Query, Set */ @@ -52,6 +52,26 @@ enum FS_INFORMATION_CLASS #define FILE_STD_INFORMATION_SIZE 22 #define FILE_END_OF_FILE_INFORMATION_SIZE 8 +/* + * File System information classes (section 2.5) + */ +enum FILE_SYSTEM_INFORMATION_CLASS +{ + FileFsVolumeInformation = 1, + FileFsSizeInformation = 3, + FileFsDeviceInformation = 4, + FileFsAttributeInformation = 5, + FileFsFullSizeInformation = 7 +}; + +/* + * Size of structs above without trailing RESERVED fields (MS-RDPEFS + * 2.2.3.3.6) + */ +#define FILE_FS_SIZE_INFORMATION_SIZE 24 +#define FILE_FS_DEVICE_INFORMATION_SIZE 8 +#define FILE_FS_FULL_SIZE_INFORMATION_SIZE 32 + /* Windows file attributes (section 2.6) */ #define W_FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define W_FILE_ATTRIBUTE_READONLY 0x00000001 diff --git a/common/ms-rdpbcgr.h b/common/ms-rdpbcgr.h index ea21ea3ce9..5e85a5a171 100644 --- a/common/ms-rdpbcgr.h +++ b/common/ms-rdpbcgr.h @@ -80,6 +80,7 @@ #define RNS_UD_CS_WANT_32BPP_SESSION 0x0002 #define RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU 0x0040 #define RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL 0x0100 +#define RNS_UD_CS_SUPPORT_SKIP_CHANNELJOIN 0x0800 /* Client Core Data: connectionType (2.2.1.3.2) */ #define CONNECTION_TYPE_MODEM 0x01 @@ -136,6 +137,9 @@ #define XR_CHANNEL_OPTION_SHOW_PROTOCOL 0x00200000 #define REMOTE_CONTROL_PERSISTENT 0x00100000 +/* Server earlyCapabilityFlags (2.2.1.4.2) */ +#define RNS_UD_SC_SKIP_CHANNELJOIN_SUPPORTED 0x00000008 + /* Server Proprietary Certificate (2.2.1.4.3.1.1) */ /* TODO: to be renamed */ #define SEC_TAG_PUBKEY 0x0006 /* BB_RSA_KEY_BLOB */ diff --git a/common/os_calls.c b/common/os_calls.c index afd5a95858..014f585658 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -1805,7 +1805,7 @@ g_set_wait_obj(tintptr obj) return 0; } fd = obj >> 16; - to_write = 4; + to_write = sizeof(buf); written = 0; while (written < to_write) { @@ -1823,12 +1823,13 @@ g_set_wait_obj(tintptr obj) return 1; } } - else if (error > 0) + else if (error > 0 && error <= (int)sizeof(buf)) { written += error; } else { + // Shouldn't get here. return 1; } } @@ -2045,18 +2046,6 @@ g_obj_wait(tintptr *read_objs, int rcount, tintptr *write_objs, int wcount, void g_random(char *data, int len) { -#if defined(_WIN32) - int index; - - srand(g_time1()); - - for (index = 0; index < len; index++) - { - data[index] = (char)rand(); /* rand returns a number between 0 and - RAND_MAX */ - } - -#else int fd; memset(data, 0x44, len); @@ -2075,8 +2064,6 @@ g_random(char *data, int len) close(fd); } - -#endif } /*****************************************************************************/ @@ -2311,15 +2298,27 @@ g_file_set_cloexec(int fd, int status) struct list * g_get_open_fds(int min, int max) { + if (min < 0) + { + min = 0; + } + struct list *result = list_create(); if (result != NULL) { if (max < 0) { - max = sysconf(_SC_OPEN_MAX); + // sysconf() returns a long. Limit it to a sane value +#define SANE_MAX 100000 + long sc_max = sysconf(_SC_OPEN_MAX); + max = (sc_max < 0) ? 0 : + (sc_max > (long)SANE_MAX) ? SANE_MAX : + sc_max; +#undef SANE_MAX } + // max and min are now both guaranteed to be >= 0 if (max > min) { struct pollfd *fds = g_new0(struct pollfd, max - min); @@ -2344,6 +2343,7 @@ g_get_open_fds(int min, int max) // Descriptor is open if (!list_add_item(result, i)) { + g_free(fds); goto nomem; } } @@ -3778,51 +3778,21 @@ g_check_user_in_group(const char *username, int gid, int *ok) #endif // HAVE_GETGROUPLIST /*****************************************************************************/ -/* returns the time since the Epoch (00:00:00 UTC, January 1, 1970), - measured in seconds. - for windows, returns the number of seconds since the machine was - started. */ -int -g_time1(void) -{ -#if defined(_WIN32) - return GetTickCount() / 1000; -#else - return time(0); -#endif -} - -/*****************************************************************************/ -/* returns the number of milliseconds since the machine was - started. */ -int -g_time2(void) +unsigned int +g_get_elapsed_ms(void) { -#if defined(_WIN32) - return (int)GetTickCount(); -#else - struct tms tm; - clock_t num_ticks = 0; - g_memset(&tm, 0, sizeof(struct tms)); - num_ticks = times(&tm); - return (int)(num_ticks * 10); -#endif -} + unsigned int result = 0; + struct timespec tp; -/*****************************************************************************/ -/* returns time in milliseconds, uses gettimeofday - does not work in win32 */ -int -g_time3(void) -{ -#if defined(_WIN32) - return 0; -#else - struct timeval tp; + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + { + result = (unsigned int)tp.tv_sec * 1000; + // POSIX 1003.1-2004 specifies that tv_nsec is a long (i.e. a + // signed type), but can only contain [0..999,999,999] + result += tp.tv_nsec / 1000000; + } - gettimeofday(&tp, 0); - return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); -#endif + return result; } /******************************************************************************/ diff --git a/common/os_calls.h b/common/os_calls.h index be06b07b45..31e8933ba2 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -393,9 +393,26 @@ int g_getgroup_info(const char *groupname, int *gid); * Primary group of username is also checked */ int g_check_user_in_group(const char *username, int gid, int *ok); -int g_time1(void); -int g_time2(void); -int g_time3(void); + +/** + * Gets elapsed milliseconds since some arbitrary point in the past + * + * The returned value is unaffected by leap-seconds or time zone changes. + * + * @return elaped ms since some arbitrary point + * + * Calculate the duration of a task by calling this routine before and + * after the task, and subtracting the two values. + * + * The value wraps every so often (every 49.7 days on a 32-bit system), + * but as we are using unsigned arithmetic, the difference of any of these + * two values can be used to calculate elapsed time, whether-or-not a wrap + * occurs during the interval - provided of course the time being measured + * is less than the total wrap-around interval. + */ +unsigned int +g_get_elapsed_ms(void); + int g_save_to_bmp(const char *filename, char *data, int stride_bytes, int width, int height, int depth, int bits_per_pixel); void *g_shmat(int shmid); diff --git a/common/ssl_calls.c b/common/ssl_calls.c index 70d2d7c8f1..d44f878b46 100644 --- a/common/ssl_calls.c +++ b/common/ssl_calls.c @@ -1104,15 +1104,12 @@ ssl_tls_accept(struct ssl_tls *self, long ssl_protocols, return 1; } DH_free(dh); // ok to free, copied into ctx by SSL_CTX_set_tmp_dh() -#else - if (!SSL_CTX_set_dh_auto(self->ctx, 1)) - { - LOG(LOG_LEVEL_ERROR, "TLS DHE auto failed to be enabled"); - dump_ssl_error_stack(self); - return 1; - } #endif -#if defined(SSL_CTX_set_ecdh_auto) + +#if (OPENSSL_VERSION_NUMBER >= 0x10000020L) && \ + OPENSSL_VERSION_NUMBER < (0x10100000L) + // SSL_CTX_set_ecdh_auto() added in OpenSSL 1.0.2 and + // removed for OpenSSL 1.1.0 if (!SSL_CTX_set_ecdh_auto(self->ctx, 1)) { LOG(LOG_LEVEL_WARNING, "TLS ecdh auto failed to be enabled"); diff --git a/common/string_calls.c b/common/string_calls.c index dca5bc150e..77b5c87d11 100644 --- a/common/string_calls.c +++ b/common/string_calls.c @@ -444,6 +444,7 @@ g_atoix(const char *str) str += 2; base = 16; } + //coverity[OVERRUN:FALSE] return strtol(str, NULL, base); } @@ -1190,7 +1191,7 @@ utf8_get_next_char(const char **utf8str_ref, unsigned int *len_ref) /* * Macro used to parse a continuation character * @param cp Character Pointer (incremented on success) - * @param end One character past end of input string + * @param end One character past end of input string, or NULL * @param value The value we're constructing * @param finish_label Where to go in the event of an error */ #define PARSE_CONTINUATION_CHARACTER(cp, end, value, finish_label) \ @@ -1209,7 +1210,7 @@ utf8_get_next_char(const char **utf8str_ref, unsigned int *len_ref) /* Easier to work with unsigned chars and no indirection */ const unsigned char *cp = (const unsigned char *)*utf8str_ref; - const unsigned char *end = (len_ref != NULL) ? cp + *len_ref : cp + 6; + const unsigned char *end = (len_ref != NULL) ? cp + *len_ref : NULL; if (cp == end) { diff --git a/common/thread_calls.c b/common/thread_calls.c index b575d4eade..b3c8904cc9 100644 --- a/common/thread_calls.c +++ b/common/thread_calls.c @@ -33,6 +33,7 @@ #include #endif #include "arch.h" +#include "log.h" #include "thread_calls.h" #include "os_calls.h" @@ -136,11 +137,17 @@ tc_mutex_lock(tbus mutex) { #if defined(_WIN32) WaitForSingleObject((HANDLE)mutex, INFINITE); - return 0; #else - pthread_mutex_lock((pthread_mutex_t *)mutex); - return 0; + if (mutex != 0) + { + pthread_mutex_lock((pthread_mutex_t *)mutex); + } + else + { + LOG(LOG_LEVEL_ERROR, "Attempt made to lock NULL mutex"); + } #endif + return 0; } /*****************************************************************************/ diff --git a/common/trans.c b/common/trans.c index 780b4c52a7..ceaf7802df 100644 --- a/common/trans.c +++ b/common/trans.c @@ -696,7 +696,7 @@ local_connect_shim(int fd, const char *server, const char *port) /**************************************************************************//** * Waits for an asynchronous connect to complete. * @param self - Transport object - * @param start_time Start time of connect (from g_time3()) + * @param start_time Start time of connect (from g_get_elapsed_ms()) * @param timeout Total wait timeout * @return 0 - connect succeeded, 1 - Connect failed * @@ -704,10 +704,10 @@ local_connect_shim(int fd, const char *server, const char *port) * on a regular basis. */ static int -poll_for_async_connect(struct trans *self, int start_time, int timeout) +poll_for_async_connect(struct trans *self, unsigned int start_time, int timeout) { int rv = 1; - int ms_remaining = timeout - (g_time3() - start_time); + int ms_remaining = timeout - (int)(g_get_elapsed_ms() - start_time); while (ms_remaining > 0) { @@ -736,7 +736,7 @@ poll_for_async_connect(struct trans *self, int start_time, int timeout) break; } - ms_remaining = timeout - (g_time3() - start_time); + ms_remaining = timeout - (int)(g_get_elapsed_ms() - start_time); } return rv; } @@ -747,7 +747,7 @@ int trans_connect(struct trans *self, const char *server, const char *port, int timeout) { - int start_time = g_time3(); + unsigned int start_time = g_get_elapsed_ms(); int error; int ms_before_next_connect; @@ -826,7 +826,7 @@ trans_connect(struct trans *self, const char *server, const char *port, } /* Have we reached the total timeout yet? */ - int ms_left = timeout - (g_time3() - start_time); + int ms_left = timeout - (int)(g_get_elapsed_ms() - start_time); if (ms_left <= 0) { error = 1; diff --git a/common/xrdp_client_info.h b/common/xrdp_client_info.h index 45965beade..f99ef17405 100644 --- a/common/xrdp_client_info.h +++ b/common/xrdp_client_info.h @@ -206,6 +206,11 @@ struct xrdp_client_info int x11_keycode_num_lock; int x11_keycode_scroll_lock; + /* xorgxrdp: frame capture interval (milliseconds) */ + int rfx_frame_interval; + int h264_frame_interval; + int normal_frame_interval; + /* ==================================================================== */ /* Private to xrdp below this line */ /* ==================================================================== */ @@ -274,6 +279,6 @@ enum xrdp_encoder_flags /* yyyymmdd of last incompatible change to xrdp_client_info */ /* also used for changes to all the xrdp installed headers */ -#define CLIENT_INFO_CURRENT_VERSION 20240805 +#define CLIENT_INFO_CURRENT_VERSION 20241118 #endif diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 200990e0b2..0b6d4a2627 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -83,6 +83,11 @@ #define MCS_SDRQ 25 /* Send Data Request */ #define MCS_SDIN 26 /* Send Data Indication */ +/* xorgxrdp: frame capture interval (milliseconds) */ +#define DEFAULT_RFX_FRAME_INTERVAL 32 +#define DEFAULT_H264_FRAME_INTERVAL 16 +#define DEFAULT_NORMAL_FRAME_INTERVAL 40 + /****************************************************************************** * * Constants come from other Microsoft products diff --git a/common/xrdp_sockets.h b/common/xrdp_sockets.h index ebf044fff2..e98b7657fd 100644 --- a/common/xrdp_sockets.h +++ b/common/xrdp_sockets.h @@ -61,4 +61,10 @@ #define XRDP_X11RDP_STR XRDP_SOCKET_PATH "/" XRDP_X11RDP_BASE_STR #define XRDP_DISCONNECT_STR XRDP_SOCKET_PATH "/" XRDP_DISCONNECT_BASE_STR +/* Where X11 stores its Unix Domain Sockets (unlikely to change) */ +#define X11_UNIX_SOCKET_DIRECTORY "/tmp/.X11-unix" + +/* fullpath to an X11 display socket */ +#define X11_UNIX_SOCKET_STR X11_UNIX_SOCKET_DIRECTORY "/X%d" + #endif diff --git a/configure.ac b/configure.ac index 67fb2b52e1..3f67704928 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.69]) AC_INIT([xrdp], [0.10.80], [xrdp-devel@googlegroups.com]) -AC_DEFINE([VERSION_YEAR], 2024, [Copyright year]) +AC_DEFINE([VERSION_YEAR], 2025, [Copyright year]) AC_CONFIG_HEADERS(config_ac.h:config_ac-h.in) AM_INIT_AUTOMAKE([1.7.2 foreign]) AC_CONFIG_MACRO_DIR([m4]) @@ -175,6 +175,10 @@ AC_ARG_ENABLE(x264, AS_HELP_STRING([--enable-x264], [Use x264 library (default: no)]), [], [enable_x264=no]) AM_CONDITIONAL(XRDP_X264, [test x$enable_x264 = xyes]) +AC_ARG_ENABLE(openh264, AS_HELP_STRING([--enable-openh264], + [Use Cisco OpenH264 library (default: no)]), + [], [enable_openh264=no]) +AM_CONDITIONAL(XRDP_OPENH264, [test x$enable_openh264 = xyes]) AC_ARG_ENABLE(painter, AS_HELP_STRING([--disable-painter], [Do not use included painter library (default: no)]), [], [enable_painter=yes]) @@ -453,8 +457,8 @@ fi # checking for fuse if test "x$enable_fuse" = "xyes" then - PKG_CHECK_MODULES([FUSE], [fuse >= 2.6], [], - [AC_MSG_ERROR([please install libfuse-dev or fuse-devel])]) + PKG_CHECK_MODULES([FUSE], [fuse3 >= 3.1.0], [], + [AC_MSG_ERROR([please install libfuse3-dev or fuse3-devel])]) fi # checking for fdk aac @@ -475,7 +479,7 @@ fi if test "x$enable_mp3lame" = "xyes" then AC_CHECK_HEADER([lame/lame.h], [], - [AC_MSG_ERROR([please install libmp3lame-dev or lamemp3-devel])]) + [AC_MSG_ERROR([please install libmp3lame-dev or lame-devel])]) fi # checking for ibus includes @@ -493,6 +497,8 @@ AS_IF( [test "x$enable_pixman" = "xyes"] , [PKG_CHECK_MODULES(PIXMAN, pixman-1 > AS_IF( [test "x$enable_x264" = "xyes"] , [PKG_CHECK_MODULES(XRDP_X264, x264 >= 0.3.0)] ) +AS_IF( [test "x$enable_openh264" = "xyes"] , [PKG_CHECK_MODULES(XRDP_OPENH264, openh264 >= 2.0.0)] ) + # checking for TurboJPEG if test "x$enable_tjpeg" = "xyes" then @@ -604,6 +610,12 @@ AC_ARG_WITH([pamconfdir], [], [with_pamconfdir="$sysconfdir/pam.d"]) AC_SUBST([pamconfdir], [$with_pamconfdir]) +AC_ARG_WITH([sysconfsubdir], + [AS_HELP_STRING([--with-sysconfsubdir=DIR], + [Use subdirectory for config files (default: xrdp)]))], + [], [with_sysconfsubdir="xrdp"]) +AC_SUBST([sysconfsubdir], [$with_sysconfsubdir]) + PKG_INSTALLDIR AC_CHECK_HEADERS([sys/prctl.h uchar.h]) @@ -668,6 +680,7 @@ echo " jpeg $enable_jpeg" echo " turbo jpeg $enable_tjpeg" echo " rfxcodec $enable_rfxcodec" echo " x264 $enable_x264" +echo " openh264 $enable_openh264" echo " painter $enable_painter" echo " pixman $enable_pixman" echo " fuse $enable_fuse" @@ -697,6 +710,7 @@ echo " exec_prefix $exec_prefix" echo " libdir $libdir" echo " bindir $bindir" echo " sysconfdir $sysconfdir" +echo " sysconfdir+subdir $sysconfdir/$sysconfsubdir" echo " pamconfdir $pamconfdir" echo " localstatedir $localstatedir" echo " runstatedir $runstatedir" diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 604704905b..235eac3f4e 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -1,5 +1,6 @@ man_MANS = \ xrdp-dis.1 \ + gfx.toml.5 \ sesman.ini.5 \ xrdp.ini.5 \ xrdp-km.toml.5 \ @@ -24,6 +25,7 @@ SUBST_VARS = sed \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@sysconfsubdir[@]|$(sysconfsubdir)|g' \ -e 's|@socketdir[@]|$(socketdir)|g' \ -e 's|@xrdpconfdir[@]|$(sysconfdir)/xrdp|g' \ -e 's|@xrdpdatadir[@]|$(datadir)/xrdp|g' \ diff --git a/docs/man/gfx.toml.5.in b/docs/man/gfx.toml.5.in new file mode 100644 index 0000000000..d0642fc5cf --- /dev/null +++ b/docs/man/gfx.toml.5.in @@ -0,0 +1,190 @@ +.\" +.TH "gfx.toml" "8" "@PACKAGE_VERSION@" "xrdp team" "" +.SH "NAME" +\fBgfx.toml\fR \- Configuration file for xrdp(8) graphics pipeline extension + +.SH "DESCRIPTION" + +This file configures the detailed settings of the Graphics Pipeline Extension +for xrdp(8). The file format is TOML. See the link in the SEE ALSO section for +TOML syntax. + +The file contains following sections: + +.TP +\fB[codec]\fR \- configure preferred codec and encoders for \fBxrdp\fR(8). + +.TP +\fB[x264]\fR \- parameters for x264 encoder. + +.TP +\fB[OpenH264]\fR \- parameters for Cisco OpenH264 encoder. + +.LP +All options and values are case-sensitive, with some exception, and are +described in detail below. + +.SH "CODEC" +\fB[codec]\fR section defines preferred codec order and encoder. The options +to be specified in this section are the following: + +.TP +\fBorder\fR = \fI\fR +Define the order in which codecs should be used, "H.264" and "RFX". +Specify as an array of strings like \fB[ "H.264", "RFX ]\fR. +Strings in the array are case-insensitive, while others are case-sensitive. + +.TP +\fBh264_encoder\fR = \fI\fR +Specify a preferred H.264 encoder, \fB"x264"\fR or \fB"OpenH264"\fR. +This parameter takes effect only when more than one encoder is +enabled at compile time. If only one H.264 encoder is enabled, the encoder +will be used regardless the value of this parameter. Defaults to \fB"x264"\fR +if not specified or if an invalid encoder is specified. The encoder name is +case-insensitive. + +.SH "X264" +\fB[x264]\fR section defines encoding parameters that will be passed to +x264 encoder. See \fBx264 --fullhelp\fR for the detailed explanations of the +parameters. The options to be specified in this section are following: + +.TP +\fBpreset\fR = \fI\fR +Select a preset encoding settings. Slower presets result in higher CPU usage +but offer better screen image quality and require lower network bandwidth. +Here are available presets: + +.B ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, +veryslow, placebo + +Presets slower than \fBmedium\fR may not suitable for use with xrdp. + +.TP +\fBtune\fR = \fI\fR +Select a tune for source or situation. \fBzerolatency\fR is most appropriate +for use with xrdp. Here are available options: + +.B film, grain, stillimage, psnr, ssim, fastdecode, zerolatency + +.TP +\fBprofile\fR = \fI\fR +Select a profile. Here are available options: + +.B main, baseline, high, high10, high422, high444 + +.TP +\fBvbv_max_bitrate\fR = \fI\fR +Set the maximum fill rate for the VBV (Video Buffering Vefifier) buffer +in kbps. + +.TP +\fBvbv_buffer_size\fR = \fI\fR +Set the size of the VBV buffer size in kilobits. + +.TP +\fBfps_num\fR = \fI\fR +Set the fps numerator. + +.TP +\fBfps_den\fR = \fI\fR +Set the fps denominator. + +.TP +\fBthreads = \fI\fR +Specify how many CPU threads to use for H.264 encoding per screen (0 for +auto). Carefully evaluate this value when configuring it to avoid exhausting +the available threads. For example, if 3 users are connected simultaneously +with dual screens, xrdp may use up to * 3 * 2 threads for H.264 +encoding. Please also note that too many threads can hurt quality. + +.SH "OPENH264" + +\fB[OpenH264]\fR section defines encoding parameters that will be passed to +OpenH264 encoder. The options to be specified in this section are following: + +.TP +\fBEnableFrameSkip\fR = \fI\fR +Allows the encoder to skip frames in order to keep the bitrate within the +limits if it is about to exceed the maximum bitrate set by MaxBitrate. + +.TP +\fBTargetBitrate\fR = \fI\fR +Sets the target average bitrate (in bps) that the encoder will attempt to +achieve throughout the encoding process. + +.TP +\fBMaxBitrate\fR = \fI\fR +Sets an upper limit of the bitrate in bps. +.TP +\fBMaxFrameRate\fR = \fI\fR +Sets the maximum frame rate that the encoder will process per second. + +.SH "CONNECTION TYPES" + +\fB[x264]\fR and \fB[OpenH264]\fR section are tables (also known as +dictionaries) that have subtables with connection types in their keys. +For example, \fB[x264.lan]\fR, \fB[OpenH264.wan]\fR. + +You can configure different parameters such as bitrate for the encoder +per connection type. Define the default parameter set first, this will be +inherited to all connection types unless explicitly overridden in each +connection type. + +List of available connection types are: + +.B lan, wan, broadband_high, satellite, broadband_low, modem, and default + +Currently, xrdp does not support connection type autodetection. If autodetect +is selected on the client side, it will be treated as if LAN is selected. + +.SH "EXAMPLES" +This is an example \fBgfx.toml\fR: + +.nf +[codec] +order = [ "H.264", "RFX" ] +h264_encoder = "x264" + +[x264.default] +preset = "ultrafast" +tune = "zerolatency" +profile = "main" # profile is forced to baseline if preset == ultrafast +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 60 +fps_den = 1 + +[x264.lan] +# inherits default, everything is same with the default +[x264.wan] +# parameters that are not explicitly overridden inherit the default values +preset = "veryfast" +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 +.fi + +There are multiple ways to represent the data structure in TOML format. The +following two representations are semantically equivalent but the latter is +discouraged due to concerns about complexity and readability. + +.nf +[x264.default] +preset = "ultrafast" +tune = "zerolatency" +.fi + +.nf +x264 = { default = { preset = "ultrafast", tune="zerolatency" } } +.fi + +.SH "SEE ALSO" + +For more information on \fBgfx.toml\fR configuration, see the wiki page. + +.UR https://github.com/neutrinolabs/xrdp/wiki/H.264-encoding +.UE + +The syntax for TOML files can be found at the following page. + +.UR https://toml.io/en/v1.0.0 +.UE diff --git a/docs/man/sesman.ini.5.in b/docs/man/sesman.ini.5.in index 6b45b7120a..ce386ebad2 100644 --- a/docs/man/sesman.ini.5.in +++ b/docs/man/sesman.ini.5.in @@ -98,7 +98,33 @@ stdout. Use for debugging only\fR It is ignored in the [ChansrvLogging] section since the channel server creates one log file per display and instead uses the -following log file naming convention \fIxrdp-chansrv.${DISPLAY}.log\fR +following log file naming convention \fIxrdp-chansrv.${DISPLAY}.log\fR. For +details of the chansrv log file location, see \fBLogFilePath\fR. + +.TP +\fBLogFilePath\fR=\fIstring\fR +Directory for storing the chansrv log file. This setting only applies to +chansrv. The sesman log file is always created in \fI@localstatedir@/log\fR. + +Created if it doesn't exist. +If first character is not a '/', this is relative to $HOME, where +chansrv is normally started. + +.RS +The following substitutions are made in this string:- + %U - Username + %u - Numeric UID + %% - Percent character + +This is most useful if you are using NFS-mounted home directories, and +wish to move the chansrv log file to the local disk. + +If this parameter isn't specified, the log file is stored in one of +the following locations :- + - $CHANSRV_LOG_PATH + - $XDG_DATA_HOME/xrdp + - $HOME/.local/share/xrdp +.RE .TP \fBLogLevel\fR=\fIlevel\fR @@ -354,6 +380,15 @@ the user). drive. To fix this, consult the docs for your chosen desktop. .RE +.TP +\fBFuseMountNameColonCharReplacement\fR=\fIstring\fR +Character to replace colon in redirected drive mount point names. +If not specified no colon will not be replaced. +If empty then colon will be replaced by null character. +If longer than one character, only first character used. +Only last colon replaced. +.RE + .TP \fBFuseDirectIO\fR=\fI[false|true]\fR Defaults to \fIfalse\fR. Set to \fItrue\fR to disable page caching in @@ -414,7 +449,7 @@ All entries in the \fB[SessionVariables]\fR section are set as environment variables in the user's session. .SH "FILES" -@sysconfdir@/xrdp/sesman.ini +@sysconfdir@/@sysconfsubdir@/sesman.ini .SH "SEE ALSO" .BR xrdp-sesman (8), diff --git a/docs/man/xrdp-chansrv.8.in b/docs/man/xrdp-chansrv.8.in index 44d5021c8c..2b642bcb86 100644 --- a/docs/man/xrdp-chansrv.8.in +++ b/docs/man/xrdp-chansrv.8.in @@ -40,7 +40,7 @@ X11 display number. Must be specified. .SH FILES .TP -.I @sysconfdir@/xrdp/sesman.ini +.I @sysconfdir@/@sysconfsubdir@/sesman.ini Contains some settings for this program. .TP .I @socketdir@/xrdp_chansrv_socket_* diff --git a/docs/man/xrdp-genkeymap.8.in b/docs/man/xrdp-genkeymap.8.in index 5e4ea63c2f..1846de6563 100644 --- a/docs/man/xrdp-genkeymap.8.in +++ b/docs/man/xrdp-genkeymap.8.in @@ -41,7 +41,7 @@ The key map information is stored in the file named \fIoutfile\fP. .SH "FILES" .TP -.I @sysconfdir@/xrdp/km-XXXXXXXX.toml +.I @sysconfdir@/@sysconfsubdir@/km-XXXXXXXX.toml Files containing the keyboard mapping for country and language \fIXXXXXXXX\fP. \fIXXXXXXXX\fP is a 8 digit hexadecimal number, representing the \fIinput locale identifier\fP. diff --git a/docs/man/xrdp-keygen.8.in b/docs/man/xrdp-keygen.8.in index 32d324588e..dd89b60f53 100644 --- a/docs/man/xrdp-keygen.8.in +++ b/docs/man/xrdp-keygen.8.in @@ -15,7 +15,7 @@ xrdp\-keygen \- xrdp RSA key generation utility .SH DESCRIPTION \fBxrdp\-keygen\fP generates the file -.I @sysconfdir@/xrdp/rsakeys.ini +.I @sysconfdir@/@sysconfsubdir@/rsakeys.ini which contains the RSA key pair used to perform authentication to remote clients. The public key is self-signed. @@ -26,14 +26,14 @@ This program takes one of the following options: Generate a new key pair. The key data is stored in the file named \fIoutfile\fP. .br -If \fBauto\fP is used as \fIoutfile\fP, the default file \fI@sysconfdir@/xrdp/rsakeys.ini\fP gets created if it does not yet exists. +If \fBauto\fP is used as \fIoutfile\fP, the default file \fI@sysconfdir@/@sysconfsubdir@/rsakeys.ini\fP gets created if it does not yet exists. .TP .B test Generate a test key pair and print information to standard output. .SH FILES .TP -.I @sysconfdir@/xrdp/rsakeys.ini +.I @sysconfdir@/@sysconfsubdir@/rsakeys.ini RSA public and private key pair used to identify this XRDP server. .SH SEE ALSO diff --git a/docs/man/xrdp-km.toml.5.in b/docs/man/xrdp-km.toml.5.in index a9dbc4322d..d3a05fb078 100644 --- a/docs/man/xrdp-km.toml.5.in +++ b/docs/man/xrdp-km.toml.5.in @@ -4,7 +4,7 @@ \fBxrdp-km.toml\fR \- \fBxrdp\fP key mapping file .SH "DESCRIPTION" -Key mapping files are located at \fB@sysconfdir@/xrdp/km-XXXXXXXX.toml\fP +Key mapping files are located at \fB@sysconfdir@/@sysconfsubdir@/km-XXXXXXXX.toml\fP where \fBXXXXXXXX\fP is the input locale identifier sent by the RDP client. The key mapping files are used to translate RDP scan codes into one of diff --git a/docs/man/xrdp-sesman.8.in b/docs/man/xrdp-sesman.8.in index 379b92b567..c7eb218c3a 100644 --- a/docs/man/xrdp-sesman.8.in +++ b/docs/man/xrdp-sesman.8.in @@ -50,7 +50,7 @@ to be used primarily for testing or for unusual configurations. .P .RS If you use this option, be aware that you will have to have a -\fB@sysconfdir@/xrdp/sesman.ini\fR in place too, as a few elements of +\fB@sysconfdir@/@sysconfsubdir@/sesman.ini\fR in place too, as a few elements of the system (notably \fBxrdp(8)\fR and \fBxrdp\-chansrv(8)\fR) will want to read it. .RE @@ -64,7 +64,7 @@ not running \fBxrdp\-sesman\fR as a daemon. .br @bindir@/xrdp\-sesrun .br -@sysconfdir@/xrdp/sesman.ini +@sysconfdir@/sysconfsubdir@/sesman.ini .br @localstatedir@/log/xrdp\-sesman.log .br diff --git a/docs/man/xrdp-sesrun.8.in b/docs/man/xrdp-sesrun.8.in index 34e3169e45..6f747f2db5 100644 --- a/docs/man/xrdp-sesrun.8.in +++ b/docs/man/xrdp-sesrun.8.in @@ -90,7 +90,7 @@ first. The \fBgnome\-terminal\fR utility probably won't work here. .br @bindir@/xrdp\-sesrun .br -@sysconfdir@/xrdp/sesman.ini +@sysconfdir@/@sysconfsubdir@/sesman.ini .SH "AUTHORS" Jay Sorg diff --git a/docs/man/xrdp.8.in b/docs/man/xrdp.8.in index 39e4d88656..eda919c247 100644 --- a/docs/man/xrdp.8.in +++ b/docs/man/xrdp.8.in @@ -57,7 +57,7 @@ to be used primarily for testing or for unusual configurations. .SH "FILES" @sbindir@/xrdp .br -@sysconfdir@/xrdp/xrdp.ini +@sysconfdir@/@sysconfsubdir@/xrdp.ini .br @localstatedir@/log/xrdp.log .br @@ -70,9 +70,9 @@ Simone Fedele .SH "SEE ALSO" .BR xrdp.ini (5), -.BR sesman (8), +.BR xrdp\-sesman (8), .BR sesman.ini (5), -.BR sesrun (8) +.BR xrdp\-sesrun (8) for more info on \fBxrdp\fR see .UR @xrdphomeurl@ diff --git a/docs/man/xrdp.ini.5.in b/docs/man/xrdp.ini.5.in index 28b8fc70b7..72005f5ba6 100644 --- a/docs/man/xrdp.ini.5.in +++ b/docs/man/xrdp.ini.5.in @@ -50,7 +50,7 @@ If set to \fB1\fR, \fBtrue\fR or \fByes\fR this option enables compression of bu .TP \fBkey_file\fP=\fI/path/to/private_key\fP Set location of TLS certificate and private key. They must be written in PEM format. -If not specified, defaults to \fB@sysconfdir@/xrdp/cert.pem\fP, \fB@sysconfdir@/xrdp/key.pem\fP. +If not specified, defaults to \fB@sysconfdir@/@sysconfsubdir@/cert.pem\fP, \fB@sysconfdir@/@sysconfsubdir@/key.pem\fP. This parameter is effective only if \fBsecurity_layer\fP is set to \fBtls\fP or \fBnegotiate\fP. @@ -381,20 +381,35 @@ Specifies the session type. The default, \fI0\fR, is Xvnc, and \fI20\fR is Xorg with xorgxrdp modules. .TP -\fBchansrvport\fR=\fBDISPLAY(\fR\fIn\fR\fB)\fR|\fI/path/to/domain-socket\fR +\fBchansrvport\fR=\fBDISPLAY(\fR\fIn\fR\fB)\fR|\fBDISPLAY(\fR\fIn,u\fR\fB)\fR||\fI/path/to/domain-socket\fR Asks xrdp to connect to a manually started \fBxrdp-chansrv\fR instance. This can be useful if you wish to use to use xrdp to connect to a VNC session which has been started other than by \fBxrdp-sesman\fR, as you can then make use of \fBxrdp\-chansrv\fR facilities in the VNC session. -The first form of this setting is recommended, replacing \fIn\fR with the -X11 display number of the session. +Either the first or second form of this setting is recommended. Replace +\fIn\fR with the X11 display number of the session, and (if applicable) +\fIu\fR with the numeric ID of the session. The second form is only +required if \fBxrdp\fR is unable to determine the session uid from the +other values in the connection block. .TP \fBkeycode_set\fR=\fI\fR [Xorg only] Asks for the specified keycode set to be used by the X server. Normally "evdev" or "base". The default should be correct for your system. +.TP +\fBh264_frame_interval\fR=\fI\fR +[Xorg only] Specify frame capture interval for H.264 captures in milliseconds. + +.TP +\fBrfx_frame_interval\fR=\fI\fR +[Xorg only] Specify frame capture interval for RemoteFX captures in milliseconds. + +.TP +\fBnormal_frame_interval\fR=\fI\fR +[Xorg only] Specify frame capture interval for normal captures in milliseconds. + .SH "EXAMPLES" This is an example \fBxrdp.ini\fR: @@ -411,6 +426,9 @@ password=ask ip=127.0.0.1 port=-1 code=20 +h264_frame_interval=16 +rfx_frame_interval=32 +normal_frame_interval=40 [vnc-any] name=vnc-any @@ -422,7 +440,7 @@ password={base64}cGFzc3dvcmQhCg== .fi .SH "FILES" -@sysconfdir@/xrdp/xrdp.ini +@sysconfdir@/@sysconfsubdir@/xrdp.ini .SH "SEE ALSO" .BR xrdp (8), diff --git a/fontutils/mkfv1.c b/fontutils/mkfv1.c index 9ed16a4b3b..0023992ecc 100644 --- a/fontutils/mkfv1.c +++ b/fontutils/mkfv1.c @@ -455,11 +455,11 @@ convert_mono_glyph(FT_GlyphSlot ft_glyph, unsigned int ucode, } } } - } - if (pa->sans10_compatibility != S10_OFF) - { - implement_sans10_compatibility(g, ucode); + if (pa->sans10_compatibility != S10_OFF) + { + implement_sans10_compatibility(g, ucode); + } } return g; diff --git a/genkeymap/Makefile.am b/genkeymap/Makefile.am index abf30972d0..85fb6e73ef 100644 --- a/genkeymap/Makefile.am +++ b/genkeymap/Makefile.am @@ -3,6 +3,7 @@ EXTRA_DIST = \ readme.txt AM_CPPFLAGS = \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -I$(top_srcdir)/common AM_CFLAGS = $(X_CFLAGS) diff --git a/genkeymap/dump-keymaps.sh b/genkeymap/dump-keymaps.sh index 155cbaf0b0..46f7d6c163 100755 --- a/genkeymap/dump-keymaps.sh +++ b/genkeymap/dump-keymaps.sh @@ -18,7 +18,10 @@ kbgen() desc="$2" os="$3" shift 3 - setxkbmap "$@" + if ! setxkbmap "$@"; then + echo "Failed to run setxkbmap $*" >&2 + return 1 + fi ./xrdp-genkeymap \ -c "Description: $desc" \ -c "Operating system: $os" \ @@ -65,13 +68,16 @@ if [ -z "$os" ]; then os="Unknown" fi +kbgen 0405 "cs-CZ" "$os" -model pc105 -layout cz kbgen 0406 "da-DK" "$os" -model pc105 -layout dk kbgen 0407 "de-DE" "$os" -model pc104 -layout de kbgen 0409 "en-US" "$os" -model pc104 -layout us -kbgen 10409 "en-US" "$os" -model pc104 -layout dvorak +kbgen 10409 "en-US" "$os" -model pc104 -layout us -variant dvorak +kbgen 60409 "en-US" "$os" -model pc104 -layout us -variant colemak kbgen 040a "es-ES_tradnl" "$os" -model pc105 -layout es kbgen 040b "fi-FI" "$os" -model pc105 -layout 'fi' kbgen 040c "fr-FR" "$os" -model pc105 -layout fr +kbgen 040e "hu-HU" "$os" -model pc105 -layout hu kbgen 0410 "it-IT" "$os" -model pc104 -layout it kbgen 0411 "ja-JP" "$os" -model pc105 -layout jp -variant OADG109A kbgen 0412 "ko-KR" "$os" -model pc105 -layout kr diff --git a/genkeymap/genkeymap.c b/genkeymap/genkeymap.c index badc75aa73..bf752c9839 100644 --- a/genkeymap/genkeymap.c +++ b/genkeymap/genkeymap.c @@ -92,8 +92,8 @@ usage(const char *programname, int status) { fprintf(stderr, "Usage: %s [ -k keycode_set] [-c comment] [-c comment...]" " out_filename\n", programname); - fprintf(stderr, "Example: %s -r evdev -c \"en-US pc104 keyboard\"" - " /etc/xrdp/km-00000409.toml\n", programname); + fprintf(stderr, "Example: %s -r evdev -c \"en-US pc104 keyboard\" ", programname); + fprintf(stderr, XRDP_CFG_PATH "/km-00000409.toml\n"); exit(status); } @@ -317,6 +317,34 @@ output_file_section(FILE *outf, } +/*****************************************************************************/ +/** + * Determine is caps-lock is supported. Some layouts (e.g. Colemak) do not + * support this key. + * + * @param dpy X display + * @reurn boolean + */ +static int +is_caps_lock_supported(Display *dpy) +{ + char dummy[4]; + KeySym ks; + XKeyPressedEvent e = + { + .type = KeyPress, + .serial = 16, + .send_event = True, + .display = dpy, + .state = 0, + .keycode = scancode_to_x11_keycode(SCANCODE_CAPS_KEY), + .same_screen = True + }; + + (void)XLookupString(&e, dummy, sizeof(dummy), &ks, NULL); + return (ks == XK_Caps_Lock || ks == XK_Eisu_toggle); +} + /*****************************************************************************/ /** * Main @@ -345,6 +373,7 @@ int main(int argc, char **argv) const char *keycode_set = NULL; struct kbd_info *kbd_info = NULL; int status = 1; + int caps_lock_supported; setlocale(LC_CTYPE, ""); if (strrchr(argv[0], '/') != NULL) @@ -418,6 +447,8 @@ int main(int argc, char **argv) goto finish; } + caps_lock_supported = is_caps_lock_supported(dpy); + fprintf(outf, "# Created by %s V" PACKAGE_VERSION "\n# Key code set: %s\n", programname, keycode_set); @@ -430,10 +461,18 @@ int main(int argc, char **argv) } fprintf(outf, "\n[General]\nversion=" KEYMAP_FILE_FORMAT_VERSION "\n"); + fprintf(outf, "caps_lock_supported=%s\n", + (caps_lock_supported) ? "true" : "false"); for (idx = 0; idx < NUM_STATES; idx++) /* Sections and states */ { - output_file_section(outf, dpy, sections[idx], states[idx]); + int mod_state = states[idx]; + if (!caps_lock_supported && ((mod_state & 2) != 0)) + { + // Skip this section as it's for caps lock + continue; + } + output_file_section(outf, dpy, sections[idx], mod_state); } status = 0; // Successful run diff --git a/instfiles/Makefile.am b/instfiles/Makefile.am index 9fd5d3d512..e4a1c67b75 100644 --- a/instfiles/Makefile.am +++ b/instfiles/Makefile.am @@ -13,6 +13,7 @@ CLEANFILES= \ SUBST_VARS = sed \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@sysconfsubdir[@]|$(sysconfsubdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' subst_verbose = $(subst_verbose_@AM_V@) @@ -26,15 +27,17 @@ SUFFIXES = .in # # files for all platforms # -startscriptdir=$(sysconfdir)/xrdp +startscriptdir=$(sysconfdir)/$(sysconfsubdir) dist_startscript_DATA = \ + km-00000405.toml \ km-00000406.toml \ km-00000407.toml \ km-00000409.toml \ km-0000040a.toml \ km-0000040b.toml \ km-0000040c.toml \ + km-0000040e.toml \ km-00000410.toml \ km-00000411.toml \ km-00000412.toml \ @@ -51,6 +54,7 @@ dist_startscript_DATA = \ km-00000816.toml \ km-0000100c.toml \ km-00010409.toml \ + km-00060409.toml \ km-19360409.toml # diff --git a/instfiles/km-00000405.toml b/instfiles/km-00000405.toml new file mode 100644 index 0000000000..5c5e53d216 --- /dev/null +++ b/instfiles/km-00000405.toml @@ -0,0 +1,1015 @@ +# Created by xrdp-genkeymap V0.10.80 +# Key code set: evdev+aliases(qwertz) +# setxkbmap -rules evdev -model pc105 -layout cz +# Description: cs-CZ +# Operating system: Ubuntu 22.04.5 LTS + +[General] +version=2 +caps_lock_supported=true + +[noshift] +01="65307:U+001B" # Escape +02="43:U+002B" # plus +03="492:U+011B" # ecaron +04="441:U+0161" # scaron +05="488:U+010D" # ccaron +06="504:U+0159" # rcaron +07="446:U+017E" # zcaron +08="253:U+00FD" # yacute +09="225:U+00E1" # aacute +0A="237:U+00ED" # iacute +0B="233:U+00E9" # eacute +0C="61:U+003D" # equal +0D="65105:U+00B4" # dead_acute +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="113:U+0071" # q +11="119:U+0077" # w +12="101:U+0065" # e +13="114:U+0072" # r +14="116:U+0074" # t +15="122:U+007A" # z +16="117:U+0075" # u +17="105:U+0069" # i +18="111:U+006F" # o +19="112:U+0070" # p +1A="250:U+00FA" # uacute +1B="41:U+0029" # parenright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="97:U+0061" # a +1F="115:U+0073" # s +20="100:U+0064" # d +21="102:U+0066" # f +22="103:U+0067" # g +23="104:U+0068" # h +24="106:U+006A" # j +25="107:U+006B" # k +26="108:U+006C" # l +27="505:U+016F" # uring +28="167:U+00A7" # section +29="59:U+003B" # semicolon +2A="65505" # Shift_L +2B="65111:U+00A8" # dead_diaeresis +2C="121:U+0079" # y +2D="120:U+0078" # x +2E="99:U+0063" # c +2F="118:U+0076" # v +30="98:U+0062" # b +31="110:U+006E" # n +32="109:U+006D" # m +33="44:U+002C" # comma +34="46:U+002E" # period +35="45:U+002D" # minus +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="92:U+005C" # backslash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shift] +01="65307:U+001B" # Escape +02="49:U+0031" # 1 +03="50:U+0032" # 2 +04="51:U+0033" # 3 +05="52:U+0034" # 4 +06="53:U+0035" # 5 +07="54:U+0036" # 6 +08="55:U+0037" # 7 +09="56:U+0038" # 8 +0A="57:U+0039" # 9 +0B="48:U+0030" # 0 +0C="37:U+0025" # percent +0D="65114:U+02C7" # dead_caron +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="81:U+0051" # Q +11="87:U+0057" # W +12="69:U+0045" # E +13="82:U+0052" # R +14="84:U+0054" # T +15="90:U+005A" # Z +16="85:U+0055" # U +17="73:U+0049" # I +18="79:U+004F" # O +19="80:U+0050" # P +1A="47:U+002F" # slash +1B="40:U+0028" # parenleft +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="65:U+0041" # A +1F="83:U+0053" # S +20="68:U+0044" # D +21="70:U+0046" # F +22="71:U+0047" # G +23="72:U+0048" # H +24="74:U+004A" # J +25="75:U+004B" # K +26="76:U+004C" # L +27="34:U+0022" # quotedbl +28="33:U+0021" # exclam +29="65112:U+00B0" # dead_abovering +2A="65505" # Shift_L +2B="39:U+0027" # apostrophe +2C="89:U+0059" # Y +2D="88:U+0058" # X +2E="67:U+0043" # C +2F="86:U+0056" # V +30="66:U+0042" # B +31="78:U+004E" # N +32="77:U+004D" # M +33="63:U+003F" # question +34="58:U+003A" # colon +35="95:U+005F" # underscore +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="124:U+007C" # bar +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[altgr] +01="65307:U+001B" # Escape +02="33:U+0021" # exclam +03="64:U+0040" # at +04="35:U+0023" # numbersign +05="36:U+0024" # dollar +06="37:U+0025" # percent +07="94:U+005E" # asciicircum +08="38:U+0026" # ampersand +09="42:U+002A" # asterisk +0A="123:U+007B" # braceleft +0B="125:U+007D" # braceright +0C="92:U+005C" # backslash +0D="65108:U+00AF" # dead_macron +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="92:U+005C" # backslash +11="124:U+007C" # bar +12="8364:U+20AC" # EuroSign +13="182:U+00B6" # paragraph +14="956:U+0167" # tslash +15="2299:U+2190" # leftarrow +16="2302:U+2193" # downarrow +17="2301:U+2192" # rightarrow +18="248:U+00F8" # oslash +19="254:U+00FE" # thorn +1A="91:U+005B" # bracketleft +1B="93:U+005D" # bracketright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="126:U+007E" # asciitilde +1F="496:U+0111" # dstroke +20="464:U+0110" # Dstroke +21="91:U+005B" # bracketleft +22="93:U+005D" # bracketright +23="96:U+0060" # grave +24="39:U+0027" # apostrophe +25="435:U+0142" # lstroke +26="419:U+0141" # Lstroke +27="36:U+0024" # dollar +28="39:U+0027" # apostrophe +29="96:U+0060" # grave +2A="65505" # Shift_L +2B="92:U+005C" # backslash +2C="176:U+00B0" # degree +2D="35:U+0023" # numbersign +2E="38:U+0026" # ampersand +2F="64:U+0040" # at +30="123:U+007B" # braceleft +31="125:U+007D" # braceright +32="94:U+005E" # asciicircum +33="60:U+003C" # less +34="62:U+003E" # greater +35="42:U+002A" # asterisk +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="47:U+002F" # slash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftaltgr] +01="65307:U+001B" # Escape +02="65107:U+007E" # dead_tilde +03="65114:U+02C7" # dead_caron +04="65106:U+005E" # dead_circumflex +05="65109:U+02D8" # dead_breve +06="65112:U+00B0" # dead_abovering +07="65116:U+02DB" # dead_ogonek +08="65104:U+0060" # dead_grave +09="65110:U+02D9" # dead_abovedot +0A="65105:U+00B4" # dead_acute +0B="65113:U+02DD" # dead_doubleacute +0C="65111:U+00A8" # dead_diaeresis +0D="65115:U+00B8" # dead_cedilla +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="2009:U+03A9" # Greek_OMEGA +11="419:U+0141" # Lstroke +12="69:U+0045" # E +13="174:U+00AE" # registered +14="940:U+0166" # Tslash +15="165:U+00A5" # yen +16="2300:U+2191" # uparrow +17="697:U+0131" # idotless +18="216:U+00D8" # Oslash +19="222:U+00DE" # THORN +1A="247:U+00F7" # division +1B="215:U+00D7" # multiply +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="198:U+00C6" # AE +1F="167:U+00A7" # section +20="208:U+00D0" # ETH +21="170:U+00AA" # ordfeminine +22="957:U+014A" # ENG +23="673:U+0126" # Hstroke +24="65122" # dead_horn +25="38:U+0026" # ampersand +26="419:U+0141" # Lstroke +27="65113:U+02DD" # dead_doubleacute +28="223:U+00DF" # ssharp +29="126:U+007E" # asciitilde +2A="65505" # Shift_L +2B="124:U+007C" # bar +2C="60:U+003C" # less +2D="62:U+003E" # greater +2E="169:U+00A9" # copyright +2F="2768:U+2018" # leftsinglequotemark +30="2769:U+2019" # rightsinglequotemark +31="78:U+004E" # N +32="186:U+00BA" # masculine +33="215:U+00D7" # multiply +34="247:U+00F7" # division +35="65110:U+02D9" # dead_abovedot +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="166:U+00A6" # brokenbar +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[capslock] +01="65307:U+001B" # Escape +02="43:U+002B" # plus +03="460:U+011A" # Ecaron +04="425:U+0160" # Scaron +05="456:U+010C" # Ccaron +06="472:U+0158" # Rcaron +07="430:U+017D" # Zcaron +08="221:U+00DD" # Yacute +09="193:U+00C1" # Aacute +0A="205:U+00CD" # Iacute +0B="201:U+00C9" # Eacute +0C="61:U+003D" # equal +0D="65105:U+00B4" # dead_acute +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="81:U+0051" # Q +11="87:U+0057" # W +12="69:U+0045" # E +13="82:U+0052" # R +14="84:U+0054" # T +15="90:U+005A" # Z +16="85:U+0055" # U +17="73:U+0049" # I +18="79:U+004F" # O +19="80:U+0050" # P +1A="218:U+00DA" # Uacute +1B="41:U+0029" # parenright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="65:U+0041" # A +1F="83:U+0053" # S +20="68:U+0044" # D +21="70:U+0046" # F +22="71:U+0047" # G +23="72:U+0048" # H +24="74:U+004A" # J +25="75:U+004B" # K +26="76:U+004C" # L +27="473:U+016E" # Uring +28="167:U+00A7" # section +29="59:U+003B" # semicolon +2A="65505" # Shift_L +2B="65111:U+00A8" # dead_diaeresis +2C="89:U+0059" # Y +2D="88:U+0058" # X +2E="67:U+0043" # C +2F="86:U+0056" # V +30="66:U+0042" # B +31="78:U+004E" # N +32="77:U+004D" # M +33="44:U+002C" # comma +34="46:U+002E" # period +35="45:U+002D" # minus +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="92:U+005C" # backslash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[capslockaltgr] +01="65307:U+001B" # Escape +02="33:U+0021" # exclam +03="64:U+0040" # at +04="35:U+0023" # numbersign +05="36:U+0024" # dollar +06="37:U+0025" # percent +07="94:U+005E" # asciicircum +08="38:U+0026" # ampersand +09="42:U+002A" # asterisk +0A="123:U+007B" # braceleft +0B="125:U+007D" # braceright +0C="92:U+005C" # backslash +0D="65108:U+00AF" # dead_macron +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="92:U+005C" # backslash +11="124:U+007C" # bar +12="8364:U+20AC" # EuroSign +13="182:U+00B6" # paragraph +14="940:U+0166" # Tslash +15="2299:U+2190" # leftarrow +16="2302:U+2193" # downarrow +17="2301:U+2192" # rightarrow +18="216:U+00D8" # Oslash +19="222:U+00DE" # THORN +1A="91:U+005B" # bracketleft +1B="93:U+005D" # bracketright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="126:U+007E" # asciitilde +1F="464:U+0110" # Dstroke +20="464:U+0110" # Dstroke +21="91:U+005B" # bracketleft +22="93:U+005D" # bracketright +23="96:U+0060" # grave +24="39:U+0027" # apostrophe +25="419:U+0141" # Lstroke +26="419:U+0141" # Lstroke +27="36:U+0024" # dollar +28="39:U+0027" # apostrophe +29="96:U+0060" # grave +2A="65505" # Shift_L +2B="92:U+005C" # backslash +2C="176:U+00B0" # degree +2D="35:U+0023" # numbersign +2E="38:U+0026" # ampersand +2F="64:U+0040" # at +30="123:U+007B" # braceleft +31="125:U+007D" # braceright +32="94:U+005E" # asciicircum +33="60:U+003C" # less +34="62:U+003E" # greater +35="42:U+002A" # asterisk +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="47:U+002F" # slash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftcapslock] +01="65307:U+001B" # Escape +02="49:U+0031" # 1 +03="50:U+0032" # 2 +04="51:U+0033" # 3 +05="52:U+0034" # 4 +06="53:U+0035" # 5 +07="54:U+0036" # 6 +08="55:U+0037" # 7 +09="56:U+0038" # 8 +0A="57:U+0039" # 9 +0B="48:U+0030" # 0 +0C="37:U+0025" # percent +0D="65114:U+02C7" # dead_caron +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="113:U+0071" # q +11="119:U+0077" # w +12="101:U+0065" # e +13="114:U+0072" # r +14="116:U+0074" # t +15="122:U+007A" # z +16="117:U+0075" # u +17="105:U+0069" # i +18="111:U+006F" # o +19="112:U+0070" # p +1A="47:U+002F" # slash +1B="40:U+0028" # parenleft +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="97:U+0061" # a +1F="115:U+0073" # s +20="100:U+0064" # d +21="102:U+0066" # f +22="103:U+0067" # g +23="104:U+0068" # h +24="106:U+006A" # j +25="107:U+006B" # k +26="108:U+006C" # l +27="34:U+0022" # quotedbl +28="33:U+0021" # exclam +29="65112:U+00B0" # dead_abovering +2A="65505" # Shift_L +2B="39:U+0027" # apostrophe +2C="121:U+0079" # y +2D="120:U+0078" # x +2E="99:U+0063" # c +2F="118:U+0076" # v +30="98:U+0062" # b +31="110:U+006E" # n +32="109:U+006D" # m +33="63:U+003F" # question +34="58:U+003A" # colon +35="95:U+005F" # underscore +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="124:U+007C" # bar +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftcapslockaltgr] +01="65307:U+001B" # Escape +02="65107:U+007E" # dead_tilde +03="65114:U+02C7" # dead_caron +04="65106:U+005E" # dead_circumflex +05="65109:U+02D8" # dead_breve +06="65112:U+00B0" # dead_abovering +07="65116:U+02DB" # dead_ogonek +08="65104:U+0060" # dead_grave +09="65110:U+02D9" # dead_abovedot +0A="65105:U+00B4" # dead_acute +0B="65113:U+02DD" # dead_doubleacute +0C="65111:U+00A8" # dead_diaeresis +0D="65115:U+00B8" # dead_cedilla +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="2009:U+03A9" # Greek_OMEGA +11="419:U+0141" # Lstroke +12="69:U+0045" # E +13="174:U+00AE" # registered +14="956:U+0167" # tslash +15="165:U+00A5" # yen +16="2300:U+2191" # uparrow +17="697:U+0131" # idotless +18="248:U+00F8" # oslash +19="254:U+00FE" # thorn +1A="247:U+00F7" # division +1B="215:U+00D7" # multiply +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="198:U+00C6" # AE +1F="167:U+00A7" # section +20="208:U+00D0" # ETH +21="170:U+00AA" # ordfeminine +22="957:U+014A" # ENG +23="673:U+0126" # Hstroke +24="65122" # dead_horn +25="38:U+0026" # ampersand +26="419:U+0141" # Lstroke +27="65113:U+02DD" # dead_doubleacute +29="126:U+007E" # asciitilde +2A="65505" # Shift_L +2B="124:U+007C" # bar +2C="60:U+003C" # less +2D="62:U+003E" # greater +2E="169:U+00A9" # copyright +2F="2768:U+2018" # leftsinglequotemark +30="2769:U+2019" # rightsinglequotemark +31="78:U+004E" # N +32="186:U+00BA" # masculine +33="215:U+00D7" # multiply +34="247:U+00F7" # division +35="65110:U+02D9" # dead_abovedot +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="166:U+00A6" # brokenbar +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[numlock] +47="65463:U+0037" # KP_7 +48="65464:U+0038" # KP_8 +49="65465:U+0039" # KP_9 +4A="65453:U+002D" # KP_Subtract +4B="65460:U+0034" # KP_4 +4C="65461:U+0035" # KP_5 +4D="65462:U+0036" # KP_6 +4E="65451:U+002B" # KP_Add +4F="65457:U+0031" # KP_1 +50="65458:U+0032" # KP_2 +51="65459:U+0033" # KP_3 +52="65456:U+0030" # KP_0 +53="65454:U+002E" # KP_Decimal diff --git a/instfiles/km-00000406.toml b/instfiles/km-00000406.toml index 8d52b9d25c..436c00a3ef 100644 --- a/instfiles/km-00000406.toml +++ b/instfiles/km-00000406.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout dk # Description: da-DK -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000407.toml b/instfiles/km-00000407.toml index 167d6dea7c..9ad8efdc15 100644 --- a/instfiles/km-00000407.toml +++ b/instfiles/km-00000407.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwertz) # setxkbmap -rules evdev -model pc104 -layout de # Description: de-DE -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000409.toml b/instfiles/km-00000409.toml index 701949708b..94bcbca324 100644 --- a/instfiles/km-00000409.toml +++ b/instfiles/km-00000409.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout us # Description: en-US -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000040a.toml b/instfiles/km-0000040a.toml index 3be3c3acef..c2f99036c4 100644 --- a/instfiles/km-0000040a.toml +++ b/instfiles/km-0000040a.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout es # Description: es-ES_tradnl -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000040b.toml b/instfiles/km-0000040b.toml index de4cb8d72f..2719f954ea 100644 --- a/instfiles/km-0000040b.toml +++ b/instfiles/km-0000040b.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout fi # Description: fi-FI -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000040c.toml b/instfiles/km-0000040c.toml index 9b0f50e8fc..4cd3c93b46 100644 --- a/instfiles/km-0000040c.toml +++ b/instfiles/km-0000040c.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(azerty) # setxkbmap -rules evdev -model pc105 -layout fr # Description: fr-FR -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000040e.toml b/instfiles/km-0000040e.toml new file mode 100644 index 0000000000..bfc1b46b7e --- /dev/null +++ b/instfiles/km-0000040e.toml @@ -0,0 +1,1016 @@ +# Created by xrdp-genkeymap V0.10.80 +# Key code set: evdev+aliases(qwertz) +# setxkbmap -rules evdev -model pc105 -layout hu +# Description: hu-HU +# Operating system: Ubuntu 22.04.5 LTS + +[General] +version=2 +caps_lock_supported=true + +[noshift] +01="65307:U+001B" # Escape +02="49:U+0031" # 1 +03="50:U+0032" # 2 +04="51:U+0033" # 3 +05="52:U+0034" # 4 +06="53:U+0035" # 5 +07="54:U+0036" # 6 +08="55:U+0037" # 7 +09="56:U+0038" # 8 +0A="57:U+0039" # 9 +0B="246:U+00F6" # odiaeresis +0C="252:U+00FC" # udiaeresis +0D="243:U+00F3" # oacute +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="113:U+0071" # q +11="119:U+0077" # w +12="101:U+0065" # e +13="114:U+0072" # r +14="116:U+0074" # t +15="122:U+007A" # z +16="117:U+0075" # u +17="105:U+0069" # i +18="111:U+006F" # o +19="112:U+0070" # p +1A="501:U+0151" # odoubleacute +1B="250:U+00FA" # uacute +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="97:U+0061" # a +1F="115:U+0073" # s +20="100:U+0064" # d +21="102:U+0066" # f +22="103:U+0067" # g +23="104:U+0068" # h +24="106:U+006A" # j +25="107:U+006B" # k +26="108:U+006C" # l +27="233:U+00E9" # eacute +28="225:U+00E1" # aacute +29="48:U+0030" # 0 +2A="65505" # Shift_L +2B="507:U+0171" # udoubleacute +2C="121:U+0079" # y +2D="120:U+0078" # x +2E="99:U+0063" # c +2F="118:U+0076" # v +30="98:U+0062" # b +31="110:U+006E" # n +32="109:U+006D" # m +33="44:U+002C" # comma +34="46:U+002E" # period +35="45:U+002D" # minus +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="237:U+00ED" # iacute +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shift] +01="65307:U+001B" # Escape +02="39:U+0027" # apostrophe +03="34:U+0022" # quotedbl +04="43:U+002B" # plus +05="33:U+0021" # exclam +06="37:U+0025" # percent +07="47:U+002F" # slash +08="61:U+003D" # equal +09="40:U+0028" # parenleft +0A="41:U+0029" # parenright +0B="214:U+00D6" # Odiaeresis +0C="220:U+00DC" # Udiaeresis +0D="211:U+00D3" # Oacute +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="81:U+0051" # Q +11="87:U+0057" # W +12="69:U+0045" # E +13="82:U+0052" # R +14="84:U+0054" # T +15="90:U+005A" # Z +16="85:U+0055" # U +17="73:U+0049" # I +18="79:U+004F" # O +19="80:U+0050" # P +1A="469:U+0150" # Odoubleacute +1B="218:U+00DA" # Uacute +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="65:U+0041" # A +1F="83:U+0053" # S +20="68:U+0044" # D +21="70:U+0046" # F +22="71:U+0047" # G +23="72:U+0048" # H +24="74:U+004A" # J +25="75:U+004B" # K +26="76:U+004C" # L +27="201:U+00C9" # Eacute +28="193:U+00C1" # Aacute +29="167:U+00A7" # section +2A="65505" # Shift_L +2B="475:U+0170" # Udoubleacute +2C="89:U+0059" # Y +2D="88:U+0058" # X +2E="67:U+0043" # C +2F="86:U+0056" # V +30="66:U+0042" # B +31="78:U+004E" # N +32="77:U+004D" # M +33="63:U+003F" # question +34="58:U+003A" # colon +35="95:U+005F" # underscore +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="205:U+00CD" # Iacute +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[altgr] +01="65307:U+001B" # Escape +02="126:U+007E" # asciitilde +03="65114:U+02C7" # dead_caron +04="94:U+005E" # asciicircum +05="65109:U+02D8" # dead_breve +06="65112:U+00B0" # dead_abovering +07="65116:U+02DB" # dead_ogonek +08="96:U+0060" # grave +09="65110:U+02D9" # dead_abovedot +0A="65105:U+00B4" # dead_acute +0B="65113:U+02DD" # dead_doubleacute +0C="65111:U+00A8" # dead_diaeresis +0D="65115:U+00B8" # dead_cedilla +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="92:U+005C" # backslash +11="124:U+007C" # bar +12="196:U+00C4" # Adiaeresis +13="182:U+00B6" # paragraph +14="956:U+0167" # tslash +15="2730:U+2013" # endash +16="8364:U+20AC" # EuroSign +17="205:U+00CD" # Iacute +18="2814:U+201E" # doublelowquotemark +19="2771:U+201D" # rightdoublequotemark +1A="247:U+00F7" # division +1B="215:U+00D7" # multiply +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="228:U+00E4" # adiaeresis +1F="496:U+0111" # dstroke +20="464:U+0110" # Dstroke +21="91:U+005B" # bracketleft +22="93:U+005D" # bracketright +23="689:U+0127" # hstroke +24="237:U+00ED" # iacute +25="435:U+0142" # lstroke +26="419:U+0141" # Lstroke +27="36:U+0024" # dollar +28="223:U+00DF" # ssharp +29="172:U+00AC" # notsign +2A="65505" # Shift_L +2B="164:U+00A4" # currency +2C="62:U+003E" # greater +2D="35:U+0023" # numbersign +2E="38:U+0026" # ampersand +2F="64:U+0040" # at +30="123:U+007B" # braceleft +31="125:U+007D" # braceright +32="60:U+003C" # less +33="59:U+003B" # semicolon +34="62:U+003E" # greater +35="42:U+002A" # asterisk +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="60:U+003C" # less +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftaltgr] +01="65307:U+001B" # Escape +02="65107:U+007E" # dead_tilde +03="439:U+02C7" # caron +04="65106:U+005E" # dead_circumflex +05="418:U+02D8" # breve +06="176:U+00B0" # degree +07="434:U+02DB" # ogonek +08="65104:U+0060" # dead_grave +09="511:U+02D9" # abovedot +0A="180:U+00B4" # acute +0B="445:U+02DD" # doubleacute +0C="168:U+00A8" # diaeresis +0D="184:U+00B8" # cedilla +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="2009:U+03A9" # Greek_OMEGA +11="419:U+0141" # Lstroke +12="69:U+0045" # E +13="174:U+00AE" # registered +14="940:U+0166" # Tslash +15="165:U+00A5" # yen +16="2300:U+2191" # uparrow +17="237:U+00ED" # iacute +18="216:U+00D8" # Oslash +19="222:U+00DE" # THORN +1A="65112:U+00B0" # dead_abovering +1B="65108:U+00AF" # dead_macron +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="196:U+00C4" # Adiaeresis +1F="167:U+00A7" # section +20="208:U+00D0" # ETH +21="170:U+00AA" # ordfeminine +22="957:U+014A" # ENG +23="673:U+0126" # Hstroke +24="205:U+00CD" # Iacute +25="38:U+0026" # ampersand +26="419:U+0141" # Lstroke +27="162:U+00A2" # cent +28="16785054:U+1E9E" # U1E9E +29="172:U+00AC" # notsign +2A="65505" # Shift_L +2B="65109:U+02D8" # dead_breve +2C="60:U+003C" # less +2D="62:U+003E" # greater +2E="169:U+00A9" # copyright +2F="2768:U+2018" # leftsinglequotemark +30="2769:U+2019" # rightsinglequotemark +31="78:U+004E" # N +32="186:U+00BA" # masculine +33="215:U+00D7" # multiply +34="247:U+00F7" # division +35="65110:U+02D9" # dead_abovedot +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="62:U+003E" # greater +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[capslock] +01="65307:U+001B" # Escape +02="49:U+0031" # 1 +03="50:U+0032" # 2 +04="51:U+0033" # 3 +05="52:U+0034" # 4 +06="53:U+0035" # 5 +07="54:U+0036" # 6 +08="55:U+0037" # 7 +09="56:U+0038" # 8 +0A="57:U+0039" # 9 +0B="214:U+00D6" # Odiaeresis +0C="220:U+00DC" # Udiaeresis +0D="211:U+00D3" # Oacute +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="81:U+0051" # Q +11="87:U+0057" # W +12="69:U+0045" # E +13="82:U+0052" # R +14="84:U+0054" # T +15="90:U+005A" # Z +16="85:U+0055" # U +17="73:U+0049" # I +18="79:U+004F" # O +19="80:U+0050" # P +1A="469:U+0150" # Odoubleacute +1B="218:U+00DA" # Uacute +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="65:U+0041" # A +1F="83:U+0053" # S +20="68:U+0044" # D +21="70:U+0046" # F +22="71:U+0047" # G +23="72:U+0048" # H +24="74:U+004A" # J +25="75:U+004B" # K +26="76:U+004C" # L +27="201:U+00C9" # Eacute +28="193:U+00C1" # Aacute +29="48:U+0030" # 0 +2A="65505" # Shift_L +2B="475:U+0170" # Udoubleacute +2C="89:U+0059" # Y +2D="88:U+0058" # X +2E="67:U+0043" # C +2F="86:U+0056" # V +30="66:U+0042" # B +31="78:U+004E" # N +32="77:U+004D" # M +33="44:U+002C" # comma +34="46:U+002E" # period +35="45:U+002D" # minus +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="205:U+00CD" # Iacute +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[capslockaltgr] +01="65307:U+001B" # Escape +02="126:U+007E" # asciitilde +03="65114:U+02C7" # dead_caron +04="94:U+005E" # asciicircum +05="65109:U+02D8" # dead_breve +06="65112:U+00B0" # dead_abovering +07="65116:U+02DB" # dead_ogonek +08="96:U+0060" # grave +09="65110:U+02D9" # dead_abovedot +0A="65105:U+00B4" # dead_acute +0B="65113:U+02DD" # dead_doubleacute +0C="65111:U+00A8" # dead_diaeresis +0D="65115:U+00B8" # dead_cedilla +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="92:U+005C" # backslash +11="124:U+007C" # bar +12="196:U+00C4" # Adiaeresis +13="182:U+00B6" # paragraph +14="940:U+0166" # Tslash +15="2730:U+2013" # endash +16="8364:U+20AC" # EuroSign +17="205:U+00CD" # Iacute +18="2814:U+201E" # doublelowquotemark +19="2771:U+201D" # rightdoublequotemark +1A="247:U+00F7" # division +1B="215:U+00D7" # multiply +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="196:U+00C4" # Adiaeresis +1F="464:U+0110" # Dstroke +20="464:U+0110" # Dstroke +21="91:U+005B" # bracketleft +22="93:U+005D" # bracketright +23="673:U+0126" # Hstroke +24="205:U+00CD" # Iacute +25="419:U+0141" # Lstroke +26="419:U+0141" # Lstroke +27="36:U+0024" # dollar +28="16785054:U+1E9E" # U1E9E +29="172:U+00AC" # notsign +2A="65505" # Shift_L +2B="164:U+00A4" # currency +2C="62:U+003E" # greater +2D="35:U+0023" # numbersign +2E="38:U+0026" # ampersand +2F="64:U+0040" # at +30="123:U+007B" # braceleft +31="125:U+007D" # braceright +32="60:U+003C" # less +33="59:U+003B" # semicolon +34="62:U+003E" # greater +35="42:U+002A" # asterisk +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="60:U+003C" # less +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftcapslock] +01="65307:U+001B" # Escape +02="39:U+0027" # apostrophe +03="34:U+0022" # quotedbl +04="43:U+002B" # plus +05="33:U+0021" # exclam +06="37:U+0025" # percent +07="47:U+002F" # slash +08="61:U+003D" # equal +09="40:U+0028" # parenleft +0A="41:U+0029" # parenright +0B="246:U+00F6" # odiaeresis +0C="252:U+00FC" # udiaeresis +0D="243:U+00F3" # oacute +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="113:U+0071" # q +11="119:U+0077" # w +12="101:U+0065" # e +13="114:U+0072" # r +14="116:U+0074" # t +15="122:U+007A" # z +16="117:U+0075" # u +17="105:U+0069" # i +18="111:U+006F" # o +19="112:U+0070" # p +1A="501:U+0151" # odoubleacute +1B="250:U+00FA" # uacute +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="97:U+0061" # a +1F="115:U+0073" # s +20="100:U+0064" # d +21="102:U+0066" # f +22="103:U+0067" # g +23="104:U+0068" # h +24="106:U+006A" # j +25="107:U+006B" # k +26="108:U+006C" # l +27="233:U+00E9" # eacute +28="225:U+00E1" # aacute +29="167:U+00A7" # section +2A="65505" # Shift_L +2B="507:U+0171" # udoubleacute +2C="121:U+0079" # y +2D="120:U+0078" # x +2E="99:U+0063" # c +2F="118:U+0076" # v +30="98:U+0062" # b +31="110:U+006E" # n +32="109:U+006D" # m +33="63:U+003F" # question +34="58:U+003A" # colon +35="95:U+005F" # underscore +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="237:U+00ED" # iacute +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftcapslockaltgr] +01="65307:U+001B" # Escape +02="65107:U+007E" # dead_tilde +03="439:U+02C7" # caron +04="65106:U+005E" # dead_circumflex +05="418:U+02D8" # breve +06="176:U+00B0" # degree +07="434:U+02DB" # ogonek +08="65104:U+0060" # dead_grave +09="511:U+02D9" # abovedot +0A="180:U+00B4" # acute +0B="445:U+02DD" # doubleacute +0C="168:U+00A8" # diaeresis +0D="184:U+00B8" # cedilla +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="2009:U+03A9" # Greek_OMEGA +11="419:U+0141" # Lstroke +12="69:U+0045" # E +13="174:U+00AE" # registered +14="956:U+0167" # tslash +15="165:U+00A5" # yen +16="2300:U+2191" # uparrow +17="205:U+00CD" # Iacute +18="216:U+00D8" # Oslash +19="222:U+00DE" # THORN +1A="65112:U+00B0" # dead_abovering +1B="65108:U+00AF" # dead_macron +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="228:U+00E4" # adiaeresis +1F="167:U+00A7" # section +20="208:U+00D0" # ETH +21="170:U+00AA" # ordfeminine +22="957:U+014A" # ENG +23="689:U+0127" # hstroke +24="237:U+00ED" # iacute +25="38:U+0026" # ampersand +26="419:U+0141" # Lstroke +27="162:U+00A2" # cent +28="223:U+00DF" # ssharp +29="172:U+00AC" # notsign +2A="65505" # Shift_L +2B="65109:U+02D8" # dead_breve +2C="60:U+003C" # less +2D="62:U+003E" # greater +2E="169:U+00A9" # copyright +2F="2768:U+2018" # leftsinglequotemark +30="2769:U+2019" # rightsinglequotemark +31="78:U+004E" # N +32="186:U+00BA" # masculine +33="215:U+00D7" # multiply +34="247:U+00F7" # division +35="65110:U+02D9" # dead_abovedot +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65509" # Caps_Lock +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="62:U+003E" # greater +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[numlock] +47="65463:U+0037" # KP_7 +48="65464:U+0038" # KP_8 +49="65465:U+0039" # KP_9 +4A="65453:U+002D" # KP_Subtract +4B="65460:U+0034" # KP_4 +4C="65461:U+0035" # KP_5 +4D="65462:U+0036" # KP_6 +4E="65451:U+002B" # KP_Add +4F="65457:U+0031" # KP_1 +50="65458:U+0032" # KP_2 +51="65459:U+0033" # KP_3 +52="65456:U+0030" # KP_0 +53="65452:U+002C" # KP_Separator diff --git a/instfiles/km-00000410.toml b/instfiles/km-00000410.toml index 4f53b9c3a6..3e9c91df02 100644 --- a/instfiles/km-00000410.toml +++ b/instfiles/km-00000410.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout it # Description: it-IT -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000411.toml b/instfiles/km-00000411.toml index 3eca81beea..9d4921dff6 100644 --- a/instfiles/km-00000411.toml +++ b/instfiles/km-00000411.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout jp -variant OADG109A # Description: ja-JP -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000412.toml b/instfiles/km-00000412.toml index 1a745253f4..8675569eca 100644 --- a/instfiles/km-00000412.toml +++ b/instfiles/km-00000412.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout kr # Description: ko-KR -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000414.toml b/instfiles/km-00000414.toml index 7786caed40..e534dc430e 100644 --- a/instfiles/km-00000414.toml +++ b/instfiles/km-00000414.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout no # Description: nb-NO -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000415.toml b/instfiles/km-00000415.toml index 390b87c41b..b8fc33eb76 100644 --- a/instfiles/km-00000415.toml +++ b/instfiles/km-00000415.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout pl # Description: pl-PL -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000416.toml b/instfiles/km-00000416.toml index 29d2072f4d..c4d9622f1b 100644 --- a/instfiles/km-00000416.toml +++ b/instfiles/km-00000416.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout br # Description: pt-BR -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000419.toml b/instfiles/km-00000419.toml index 6251bb75eb..89be89d1b6 100644 --- a/instfiles/km-00000419.toml +++ b/instfiles/km-00000419.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout ru # Description: ru-RU -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000041d.toml b/instfiles/km-0000041d.toml index 4a3211121e..d460256c77 100644 --- a/instfiles/km-0000041d.toml +++ b/instfiles/km-0000041d.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout se # Description: sv-SE -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000807.toml b/instfiles/km-00000807.toml index 34b6fee168..d6d94b5e2b 100644 --- a/instfiles/km-00000807.toml +++ b/instfiles/km-00000807.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwertz) # setxkbmap -rules evdev -model pc105 -layout ch # Description: de-CH -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000809.toml b/instfiles/km-00000809.toml index 97b9bf9983..7f3f1e8104 100644 --- a/instfiles/km-00000809.toml +++ b/instfiles/km-00000809.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout gb # Description: en-GB -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000080a.toml b/instfiles/km-0000080a.toml index 8157ffc861..ebdf1f7fc4 100644 --- a/instfiles/km-0000080a.toml +++ b/instfiles/km-0000080a.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout latam # Description: es-MX -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000080c.toml b/instfiles/km-0000080c.toml index c99f073134..ef2b4d4f01 100644 --- a/instfiles/km-0000080c.toml +++ b/instfiles/km-0000080c.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(azerty) # setxkbmap -rules evdev -model pc105 -layout be -variant oss # Description: fr-BE -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000813.toml b/instfiles/km-00000813.toml index 5756717b77..53001b46d7 100644 --- a/instfiles/km-00000813.toml +++ b/instfiles/km-00000813.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(azerty) # setxkbmap -rules evdev -model pc105 -layout be # Description: nl-BE -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00000816.toml b/instfiles/km-00000816.toml index 05e6678987..b237917422 100644 --- a/instfiles/km-00000816.toml +++ b/instfiles/km-00000816.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc104 -layout pt # Description: pt-PT -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-0000100c.toml b/instfiles/km-0000100c.toml index 80b85212aa..0ad3a5f1b8 100644 --- a/instfiles/km-0000100c.toml +++ b/instfiles/km-0000100c.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwertz) # setxkbmap -rules evdev -model pc105 -layout ch -variant fr # Description: fr-CH -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00010409.toml b/instfiles/km-00010409.toml index 654a26f065..30f8d52d69 100644 --- a/instfiles/km-00010409.toml +++ b/instfiles/km-00010409.toml @@ -1,11 +1,12 @@ # Created by xrdp-genkeymap V0.10.80 # Key code set: evdev+aliases(qwerty) -# setxkbmap -rules evdev -model pc104 -layout dvorak +# setxkbmap -rules evdev -model pc104 -layout us -variant dvorak # Description: en-US -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/km-00060409.toml b/instfiles/km-00060409.toml new file mode 100644 index 0000000000..edef50b3dc --- /dev/null +++ b/instfiles/km-00060409.toml @@ -0,0 +1,520 @@ +# Created by xrdp-genkeymap V0.10.80 +# Key code set: evdev+aliases(qwerty) +# setxkbmap -rules evdev -model pc104 -layout us -variant colemak +# Description: en-US +# Operating system: Ubuntu 22.04.5 LTS + +[General] +version=2 +caps_lock_supported=false + +[noshift] +01="65307:U+001B" # Escape +02="49:U+0031" # 1 +03="50:U+0032" # 2 +04="51:U+0033" # 3 +05="52:U+0034" # 4 +06="53:U+0035" # 5 +07="54:U+0036" # 6 +08="55:U+0037" # 7 +09="56:U+0038" # 8 +0A="57:U+0039" # 9 +0B="48:U+0030" # 0 +0C="45:U+002D" # minus +0D="61:U+003D" # equal +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="113:U+0071" # q +11="119:U+0077" # w +12="102:U+0066" # f +13="112:U+0070" # p +14="103:U+0067" # g +15="106:U+006A" # j +16="108:U+006C" # l +17="117:U+0075" # u +18="121:U+0079" # y +19="59:U+003B" # semicolon +1A="91:U+005B" # bracketleft +1B="93:U+005D" # bracketright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="97:U+0061" # a +1F="114:U+0072" # r +20="115:U+0073" # s +21="116:U+0074" # t +22="100:U+0064" # d +23="104:U+0068" # h +24="110:U+006E" # n +25="101:U+0065" # e +26="105:U+0069" # i +27="111:U+006F" # o +28="39:U+0027" # apostrophe +29="96:U+0060" # grave +2A="65505" # Shift_L +2B="92:U+005C" # backslash +2C="122:U+007A" # z +2D="120:U+0078" # x +2E="99:U+0063" # c +2F="118:U+0076" # v +30="98:U+0062" # b +31="107:U+006B" # k +32="109:U+006D" # m +33="44:U+002C" # comma +34="46:U+002E" # period +35="47:U+002F" # slash +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65288:U+0008" # BackSpace +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="45:U+002D" # minus +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shift] +01="65307:U+001B" # Escape +02="33:U+0021" # exclam +03="64:U+0040" # at +04="35:U+0023" # numbersign +05="36:U+0024" # dollar +06="37:U+0025" # percent +07="94:U+005E" # asciicircum +08="38:U+0026" # ampersand +09="42:U+002A" # asterisk +0A="40:U+0028" # parenleft +0B="41:U+0029" # parenright +0C="95:U+005F" # underscore +0D="43:U+002B" # plus +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="81:U+0051" # Q +11="87:U+0057" # W +12="70:U+0046" # F +13="80:U+0050" # P +14="71:U+0047" # G +15="74:U+004A" # J +16="76:U+004C" # L +17="85:U+0055" # U +18="89:U+0059" # Y +19="58:U+003A" # colon +1A="123:U+007B" # braceleft +1B="125:U+007D" # braceright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="65:U+0041" # A +1F="82:U+0052" # R +20="83:U+0053" # S +21="84:U+0054" # T +22="68:U+0044" # D +23="72:U+0048" # H +24="78:U+004E" # N +25="69:U+0045" # E +26="73:U+0049" # I +27="79:U+004F" # O +28="34:U+0022" # quotedbl +29="126:U+007E" # asciitilde +2A="65505" # Shift_L +2B="124:U+007C" # bar +2C="90:U+005A" # Z +2D="88:U+0058" # X +2E="67:U+0043" # C +2F="86:U+0056" # V +30="66:U+0042" # B +31="75:U+004B" # K +32="77:U+004D" # M +33="60:U+003C" # less +34="62:U+003E" # greater +35="63:U+003F" # question +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="32:U+0020" # space +3A="65288:U+0008" # BackSpace +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="95:U+005F" # underscore +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[altgr] +01="65307:U+001B" # Escape +02="161:U+00A1" # exclamdown +03="186:U+00BA" # masculine +04="170:U+00AA" # ordfeminine +05="162:U+00A2" # cent +06="8364:U+20AC" # EuroSign +07="689:U+0127" # hstroke +08="240:U+00F0" # eth +09="254:U+00FE" # thorn +0A="2768:U+2018" # leftsinglequotemark +0B="2769:U+2019" # rightsinglequotemark +0C="2730:U+2013" # endash +0D="215:U+00D7" # multiply +0E="65288:U+0008" # BackSpace +0F="65289:U+0009" # Tab +10="228:U+00E4" # adiaeresis +11="229:U+00E5" # aring +12="227:U+00E3" # atilde +13="248:U+00F8" # oslash +14="65116:U+02DB" # dead_ogonek +15="496:U+0111" # dstroke +16="435:U+0142" # lstroke +17="250:U+00FA" # uacute +18="252:U+00FC" # udiaeresis +19="246:U+00F6" # odiaeresis +1A="171:U+00AB" # guillemotleft +1B="187:U+00BB" # guillemotright +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="225:U+00E1" # aacute +1F="65104:U+0060" # dead_grave +20="223:U+00DF" # ssharp +21="65105:U+00B4" # dead_acute +22="65111:U+00A8" # dead_diaeresis +23="65114:U+02C7" # dead_caron +24="241:U+00F1" # ntilde +25="233:U+00E9" # eacute +26="237:U+00ED" # iacute +27="243:U+00F3" # oacute +28="245:U+00F5" # otilde +29="65107:U+007E" # dead_tilde +2A="65505" # Shift_L +2B="126:U+007E" # asciitilde +2C="230:U+00E6" # ae +2D="65106:U+005E" # dead_circumflex +2E="231:U+00E7" # ccedilla +2F="5053:U+0153" # oe +30="65109:U+02D8" # dead_breve +31="65112:U+00B0" # dead_abovering +32="65108:U+00AF" # dead_macron +33="65115:U+00B8" # dead_cedilla +34="65110:U+02D9" # dead_abovedot +35="191:U+00BF" # questiondown +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65513" # Alt_L +39="32:U+0020" # space +3A="65288:U+0008" # BackSpace +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="2730:U+2013" # endash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025044" # XF86AudioPlay +E0_24="269025045" # XF86AudioStop +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[shiftaltgr] +01="65307:U+001B" # Escape +02="185:U+00B9" # onesuperior +03="178:U+00B2" # twosuperior +04="179:U+00B3" # threesuperior +05="163:U+00A3" # sterling +06="165:U+00A5" # yen +07="673:U+0126" # Hstroke +08="208:U+00D0" # ETH +09="222:U+00DE" # THORN +0A="2770:U+201C" # leftdoublequotemark +0B="2771:U+201D" # rightdoublequotemark +0C="2729:U+2014" # emdash +0D="247:U+00F7" # division +0E="65288:U+0008" # BackSpace +0F="65056" # ISO_Left_Tab +10="196:U+00C4" # Adiaeresis +11="197:U+00C5" # Aring +12="195:U+00C3" # Atilde +13="216:U+00D8" # Oslash +14="126:U+007E" # asciitilde +15="464:U+0110" # Dstroke +16="419:U+0141" # Lstroke +17="218:U+00DA" # Uacute +18="220:U+00DC" # Udiaeresis +19="214:U+00D6" # Odiaeresis +1A="16785465:U+2039" # U2039 +1B="16785466:U+203A" # U203A +1C="65293:U+000D" # Return +1D="65507" # Control_L +1E="193:U+00C1" # Aacute +1F="126:U+007E" # asciitilde +20="16785054:U+1E9E" # U1E9E +21="65113:U+02DD" # dead_doubleacute +22="126:U+007E" # asciitilde +23="126:U+007E" # asciitilde +24="209:U+00D1" # Ntilde +25="201:U+00C9" # Eacute +26="205:U+00CD" # Iacute +27="211:U+00D3" # Oacute +28="213:U+00D5" # Otilde +29="126:U+007E" # asciitilde +2A="65505" # Shift_L +2B="126:U+007E" # asciitilde +2C="198:U+00C6" # AE +2D="126:U+007E" # asciitilde +2E="199:U+00C7" # Ccedilla +2F="5052:U+0152" # OE +30="126:U+007E" # asciitilde +31="126:U+007E" # asciitilde +32="126:U+007E" # asciitilde +33="126:U+007E" # asciitilde +34="126:U+007E" # asciitilde +35="126:U+007E" # asciitilde +36="65506" # Shift_R +37="65450:U+002A" # KP_Multiply +38="65511" # Meta_L +39="160:U+00A0" # nobreakspace +3A="65288:U+0008" # BackSpace +3B="65470" # F1 +3C="65471" # F2 +3D="65472" # F3 +3E="65473" # F4 +3F="65474" # F5 +40="65475" # F6 +41="65476" # F7 +42="65477" # F8 +43="65478" # F9 +44="65479" # F10 +45="65407" # Num_Lock +46="65300" # Scroll_Lock +47="65429" # KP_Home +48="65431" # KP_Up +49="65434" # KP_Prior +4A="65453:U+002D" # KP_Subtract +4B="65430" # KP_Left +4C="65437" # KP_Begin +4D="65432" # KP_Right +4E="65451:U+002B" # KP_Add +4F="65436" # KP_End +50="65433" # KP_Down +51="65435" # KP_Next +52="65438" # KP_Insert +53="65439" # KP_Delete +56="2729:U+2014" # emdash +57="65480" # F11 +58="65481" # F12 +70="65319" # Hiragana_Katakana +79="65315" # Henkan_Mode +7B="65314" # Muhenkan +7E="65454:U+002E" # KP_Decimal +E0_10="269025046" # XF86AudioPrev +E0_19="269025047" # XF86AudioNext +E0_1C="65421:U+000D" # KP_Enter +E0_1D="65508" # Control_R +E0_20="269025042" # XF86AudioMute +E0_21="269025053" # XF86Calculator +E0_22="269025073" # XF86AudioPause +E0_24="269025068" # XF86Eject +E0_2E="269025041" # XF86AudioLowerVolume +E0_30="269025043" # XF86AudioRaiseVolume +E0_32="269025048" # XF86HomePage +E0_35="65455:U+002F" # KP_Divide +E0_37="65377" # Print +E0_38="65027" # ISO_Level3_Shift +E0_47="65360" # Home +E0_48="65362" # Up +E0_49="65365" # Prior +E0_4B="65361" # Left +E0_4D="65363" # Right +E0_4F="65367" # End +E0_50="65364" # Down +E0_51="65366" # Next +E0_52="65379" # Insert +E0_53="65535:U+007F" # Delete +E0_5B="65515" # Super_L +E0_5C="65516" # Super_R +E0_5D="65383" # Menu +E0_65="269025051" # XF86Search +E0_66="269025072" # XF86Favorites +E0_6B="269025075" # XF86MyComputer +E0_6C="269025049" # XF86Mail +E1_1D="65299" # Pause + +[numlock] +47="65463:U+0037" # KP_7 +48="65464:U+0038" # KP_8 +49="65465:U+0039" # KP_9 +4A="65453:U+002D" # KP_Subtract +4B="65460:U+0034" # KP_4 +4C="65461:U+0035" # KP_5 +4D="65462:U+0036" # KP_6 +4E="65451:U+002B" # KP_Add +4F="65457:U+0031" # KP_1 +50="65458:U+0032" # KP_2 +51="65459:U+0033" # KP_3 +52="65456:U+0030" # KP_0 +53="65454:U+002E" # KP_Decimal diff --git a/instfiles/km-19360409.toml b/instfiles/km-19360409.toml index 679ed76356..905fac2489 100644 --- a/instfiles/km-19360409.toml +++ b/instfiles/km-19360409.toml @@ -2,10 +2,11 @@ # Key code set: evdev+aliases(qwerty) # setxkbmap -rules evdev -model pc105 -layout us -variant dvp -option "" -option compose:102 -option caps:shift -option numpad:sg -option numpad:shift3 -option keypad:hex -option keypad:atm -option kpdl:semi -option lv3:ralt_alt # Description: en-US -# Operating system: Ubuntu 22.04.4 LTS +# Operating system: Ubuntu 22.04.5 LTS [General] version=2 +caps_lock_supported=true [noshift] 01="65307:U+001B" # Escape diff --git a/instfiles/pam.d/xrdp-sesman.debian b/instfiles/pam.d/xrdp-sesman.debian index 2dd2ecb8b2..b97711bbc1 100644 --- a/instfiles/pam.d/xrdp-sesman.debian +++ b/instfiles/pam.d/xrdp-sesman.debian @@ -9,6 +9,8 @@ auth required pam_env.so readenv=1 envfile=/etc/default/locale @include common-password +# Ensure resource limits are applied +session required pam_limits.so # Set the loginuid process attribute. session required pam_loginuid.so # Update wtmp/lastlog diff --git a/instfiles/pulse/Makefile.am b/instfiles/pulse/Makefile.am index 72b80d10af..00c7a000aa 100644 --- a/instfiles/pulse/Makefile.am +++ b/instfiles/pulse/Makefile.am @@ -1,2 +1,2 @@ -pulsedir = $(sysconfdir)/xrdp/pulse +pulsedir = $(sysconfdir)/$(sysconfsubdir)/pulse dist_pulse_DATA = default.pa diff --git a/keygen/Makefile.am b/keygen/Makefile.am index 2422196a58..9ee5a1b1f5 100644 --- a/keygen/Makefile.am +++ b/keygen/Makefile.am @@ -1,7 +1,7 @@ EXTRA_DIST = openssl.conf AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ @@ -15,7 +15,7 @@ xrdp_keygen_SOURCES = keygen.c xrdp_keygen_LDADD = \ $(top_builddir)/common/libcommon.la -xrdpsysconfdir = $(sysconfdir)/xrdp +xrdpsysconfdir = $(sysconfdir)/$(sysconfsubdir) install-data-hook: umask 077 && \ diff --git a/keygen/keygen.c b/keygen/keygen.c index 9b477530b1..2d06aef760 100644 --- a/keygen/keygen.c +++ b/keygen/keygen.c @@ -463,7 +463,7 @@ key_gen(const char *path_and_file_name) static int key_gen_auto(void) { - return key_gen("/etc/xrdp/rsakeys.ini"); + return key_gen(XRDP_CFG_PATH "/rsakeys.ini"); } /*****************************************************************************/ diff --git a/libipm/Makefile.am b/libipm/Makefile.am index a1a6edcdac..8da084bc74 100644 --- a/libipm/Makefile.am +++ b/libipm/Makefile.am @@ -19,6 +19,8 @@ libipm_la_SOURCES = \ ercp.c \ scp.h \ scp.c \ + scp_sync.h \ + scp_sync.c \ scp_application_types.h \ scp_application_types.c diff --git a/libipm/libipm_recv.c b/libipm/libipm_recv.c index 5842a1e0d0..5828e0ebf1 100644 --- a/libipm/libipm_recv.c +++ b/libipm/libipm_recv.c @@ -135,7 +135,7 @@ libipm_msg_in_check_available(struct trans *trans, int *available) enum libipm_status libipm_msg_in_wait_available(struct trans *trans) { - tbus wobj[1]; + tbus wobj[2]; // trans_get_wait_objs() can return at most 2 elements int ocnt = 0; enum libipm_status rv = E_LI_SUCCESS; diff --git a/libipm/scp.c b/libipm/scp.c index c090bc500e..0e96a19205 100644 --- a/libipm/scp.c +++ b/libipm/scp.c @@ -649,6 +649,46 @@ scp_get_list_sessions_response( /*****************************************************************************/ +int +scp_send_create_sockdir_request(struct trans *trans) +{ + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_CREATE_SOCKDIR_REQUEST, + NULL); +} + + +/*****************************************************************************/ + +int +scp_send_create_sockdir_response(struct trans *trans, + enum scp_create_sockdir_status status) +{ + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_CREATE_SOCKDIR_RESPONSE, + "i", status); +} + +/*****************************************************************************/ + +int +scp_get_create_sockdir_response(struct trans *trans, + enum scp_create_sockdir_status *status) +{ + int32_t i_status = 0; + int rv = libipm_msg_in_parse(trans, "i", &i_status); + if (rv == 0) + { + *status = (enum scp_create_sockdir_status)i_status; + } + + return rv; +} + +/*****************************************************************************/ + int scp_send_close_connection_request(struct trans *trans) { diff --git a/libipm/scp.h b/libipm/scp.h index ff54fd6a73..1ec51e8285 100644 --- a/libipm/scp.h +++ b/libipm/scp.h @@ -59,6 +59,9 @@ enum scp_msg_code E_SCP_LIST_SESSIONS_REQUEST, E_SCP_LIST_SESSIONS_RESPONSE, + E_SCP_CREATE_SOCKDIR_REQUEST, + E_SCP_CREATE_SOCKDIR_RESPONSE, + E_SCP_CLOSE_CONNECTION_REQUEST // No E_SCP_CLOSE_CONNECTION_RESPONSE }; @@ -411,7 +414,7 @@ scp_get_create_session_response(struct trans *trans, struct guid *guid); /** - * Send an E_LIST_SESSIONS_REQUEST (SCP client) + * Send an E_SCP_LIST_SESSIONS_REQUEST (SCP client) * * @param trans SCP transport * @return != 0 for error @@ -422,7 +425,7 @@ int scp_send_list_sessions_request(struct trans *trans); /** - * Send an E_LIST_SESSIONS_RESPONSE (SCP server) + * Send an E_SCP_LIST_SESSIONS_RESPONSE (SCP server) * * @param trans SCP transport * @param status Status of request @@ -436,7 +439,7 @@ scp_send_list_sessions_response( const struct scp_session_info *info); /** - * Parse an incoming E_LIST_SESSIONS_RESPONSE (SCP client) + * Parse an incoming E_SCP_LIST_SESSIONS_RESPONSE (SCP client) * * @param trans SCP transport * @param[out] status Status of request @@ -456,6 +459,43 @@ scp_get_list_sessions_response( enum scp_list_sessions_status *status, struct scp_session_info **info); +/** + * Send an E_SCP_CREATE_SOCKDIR_REQUEST (SCP client) + * + * @param trans SCP transport + * @return != 0 for error + * + * In some configurations, chansrv is not started by sesman. In this + * instance, it may be necessary for the unprivileged sesman process to + * ask sesman to create the sockets dir so sesman can populate it. + * + * Server replies with E_SCP_CREATE_SOCKDIR_RESPONSE + */ +int +scp_send_create_sockdir_request(struct trans *trans); + +/** + * Send an E_SCP_CREATE_SOCKDIR_RESPONSE (SCP server) + * + * @param trans SCP transport + * @param status Status of request + * @return != 0 for error + */ +int +scp_send_create_sockdir_response(struct trans *trans, + enum scp_create_sockdir_status status); + +/** + * Parse an incoming E_SCP_CREATE_SOCKDIR_RESPONSE (SCP client) + * + * @param trans SCP transport + * @param[out] status Status of request + * @return != 0 for error + */ +int +scp_get_create_sockdir_response(struct trans *trans, + enum scp_create_sockdir_status *status); + /** * Send an E_CLOSE_CONNECTION_REQUEST (SCP client) * diff --git a/libipm/scp_application_types.h b/libipm/scp_application_types.h index 89c7ca8aac..ab6e8e0e40 100644 --- a/libipm/scp_application_types.h +++ b/libipm/scp_application_types.h @@ -136,5 +136,23 @@ enum scp_list_sessions_status E_SCP_LS_NO_MEMORY }; +/** + * Status of a create sockdir message + */ +enum scp_create_sockdir_status +{ + E_SCP_CS_OK = 0, + + /** + * Client hasn't logged in yet + */ + E_SCP_CS_NOT_LOGGED_IN = 100, + + /** + * sesman failed to create the directory + */ + E_SCP_CS_OTHER_ERROR +}; + #endif /* SCP_APPLICATION_TYPES_H */ diff --git a/libipm/scp_sync.c b/libipm/scp_sync.c new file mode 100644 index 0000000000..cf565335e0 --- /dev/null +++ b/libipm/scp_sync.c @@ -0,0 +1,210 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022 + * + * Licensed 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. + */ + +/** + * + * @file scp_sync.c + * @brief scp definitions (synchronous calls) + * @author Matt Burt + * + */ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +//nclude "tools_common.h" +//nclude "trans.h" +#include "os_calls.h" +#include "log.h" +#include "scp.h" +#include "scp_sync.h" + +/*****************************************************************************/ +int +scp_sync_wait_specific(struct trans *t, enum scp_msg_code wait_msgno) +{ + + int rv = 0; + int available = 0; + + while (rv == 0 && !available) + { + if ((rv = scp_msg_in_wait_available(t)) != 0) + { + LOG(LOG_LEVEL_ERROR, "Error waiting on sesman transport"); + } + else + { + enum scp_msg_code reply_msgno = scp_msg_in_get_msgno(t); + + available = 1; + if (reply_msgno != wait_msgno) + { + char buff[64]; + scp_msgno_to_str(reply_msgno, buff, sizeof(buff)); + + LOG(LOG_LEVEL_WARNING, + "Ignoring unexpected message %s", buff); + scp_msg_in_reset(t); + available = 0; + } + } + } + + return rv; +} + +/*****************************************************************************/ +int +scp_sync_uds_login_request(struct trans *t) +{ + int rv = scp_send_uds_login_request(t); + if (rv == 0) + { + if ((rv = scp_sync_wait_specific(t, E_SCP_LOGIN_RESPONSE)) == 0) + { + enum scp_login_status login_result; + int server_closed; + rv = scp_get_login_response(t, &login_result, &server_closed, NULL); + if (rv == 0 && login_result != E_SCP_LOGIN_OK) + { + char msg[256]; + scp_login_status_to_str(login_result, msg, sizeof(msg)); + g_printf("Login failed; %s\n", msg); + rv = 1; + if (!server_closed) + { + (void)scp_send_close_connection_request(t); + } + } + scp_msg_in_reset(t); // Done with this message + } + + } + return rv; +} + +/*****************************************************************************/ +struct list * +scp_sync_list_sessions_request(struct trans *t) +{ + struct list *sessions = list_create(); + if (sessions == NULL) + { + LOG(LOG_LEVEL_ERROR, "Out of memory for sessions list"); + } + else + { + int end_of_list = 0; + + enum scp_list_sessions_status status; + struct scp_session_info *p; + + int rv = scp_send_list_sessions_request(t); + + sessions->auto_free = 1; + + while (rv == 0 && !end_of_list) + { + rv = scp_sync_wait_specific(t, E_SCP_LIST_SESSIONS_RESPONSE); + if (rv != 0) + { + break; + } + + rv = scp_get_list_sessions_response(t, &status, &p); + if (rv != 0) + { + break; + } + + switch (status) + { + case E_SCP_LS_SESSION_INFO: + if (!list_add_item(sessions, (tintptr)p)) + { + g_free(p); + LOG(LOG_LEVEL_ERROR, "Out of memory for session item"); + rv = 1; + } + break; + + case E_SCP_LS_END_OF_LIST: + end_of_list = 1; + break; + + default: + LOG(LOG_LEVEL_ERROR, + "Unexpected return code %d for session item", status); + rv = 1; + } + scp_msg_in_reset(t); + } + + if (rv != 0) + { + list_delete(sessions); + sessions = NULL; + } + } + + return sessions; +} + +/*****************************************************************************/ +int +scp_sync_create_sockdir_request(struct trans *t) +{ + int rv = scp_send_create_sockdir_request(t); + if (rv == 0) + { + rv = scp_sync_wait_specific(t, E_SCP_CREATE_SOCKDIR_RESPONSE); + if (rv == 0) + { + enum scp_create_sockdir_status status; + rv = scp_get_create_sockdir_response(t, &status); + if (rv == 0) + { + switch (status) + { + case E_SCP_CS_OK: + break; + + case E_SCP_CS_NOT_LOGGED_IN: + LOG(LOG_LEVEL_ERROR, "sesman reported not-logged-in"); + rv = 1; + break; + + case E_SCP_CS_OTHER_ERROR: + LOG(LOG_LEVEL_ERROR, + "sesman reported fail on create directory"); + rv = 1; + break; + } + } + scp_msg_in_reset(t); // Done with this message + if (!rv) + { + (void)scp_send_close_connection_request(t); + } + } + + } + return rv; +} diff --git a/libipm/scp_sync.h b/libipm/scp_sync.h new file mode 100644 index 0000000000..9b36ce8512 --- /dev/null +++ b/libipm/scp_sync.h @@ -0,0 +1,101 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors + * + * Licensed 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. + */ + +/** + * + * @file libipm/scp_sync.h + * @brief scp declarations (synchronous calls) + * @author Simone Fedele/ Matt Burt + * + * This module places a synchronous wrapper on top of some of the + * calls in scp.h. It is intended to be used for simple SCP applications + * which do not need to handle SCP messages along with other messages + * using the xrdp transport mechanism. + * + */ + +#ifndef SCP_SYNC_H +#define SCP_SYNC_H + +#include + +/** + * Waits on a single transport for a specific SCP message to be available for + * parsing + * + * @param trans libipm transport + * @param wait_msgno Message number to wait for + * @return != 0 for error + * + * This is a convenience function, used to implement synchronous calls. + * + * While the call is active, data-in callbacks for the transport are + * disabled. + * + * Unexpected messages are ignored and logged. + * + * Only use this call if you have nothing to do until a message + * arrives on the transport. + * - If you have other transports to service, use + * scp_msg_in_check_available() + * - If you can process any incoming message, use + * scp_msg_in_wait_available() + */ +int +scp_sync_wait_specific(struct trans *t, enum scp_msg_code wait_msgno); + +/** + * Send UDS login request to sesman and wait for answer + * + * @param t SCP transport + * @return 0 for successful login + * + * If non-zero is returned, the scp_connection has been closed (if + * appropriate) and simply remains to be deleted. + */ +int +scp_sync_uds_login_request(struct trans *t); + +/** + * Send list sessions request to sesman and wait for answer(s) + * + * @param t SCP transport + * @return list of sessions + * + * If NULL is returned, the scp_connection has been closed (if + * appropriate) and simply remains to be deleted. + * + * If NULL is not returned, the caller must call list_delete() to + * free it. + */ +struct list * +scp_sync_list_sessions_request(struct trans *t); + +/** + * Send sockdir creation request to sesman and wait for answer + * + * @param t SCP transport + * @return 0 for successful response from sesman + * + * If non-zero is returned, the scp_connection has been closed (if + * appropriate) and simply remains to be deleted. + */ +int +scp_sync_create_sockdir_request(struct trans *t); + +#endif /* SCP_SYNC_H */ diff --git a/libxrdp/Makefile.am b/libxrdp/Makefile.am index 49df6c658f..8a2c1b76ef 100644 --- a/libxrdp/Makefile.am +++ b/libxrdp/Makefile.am @@ -2,7 +2,7 @@ EXTRA_DIST = \ xrdp_surface.c AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/libxrdp/xrdp_mcs.c b/libxrdp/xrdp_mcs.c index 10e32d685d..3a056da1f9 100644 --- a/libxrdp/xrdp_mcs.c +++ b/libxrdp/xrdp_mcs.c @@ -29,8 +29,8 @@ /* Forward references */ static int -handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self, - struct stream *s, int *appid); +handle_channel_join_requests(struct xrdp_mcs *self, + struct stream *s, int *appid); /*****************************************************************************/ struct xrdp_mcs * @@ -178,7 +178,7 @@ xrdp_mcs_recv(struct xrdp_mcs *self, struct stream *s, int *chan) if (self->expecting_channel_join_requests) { - if (handle_non_tls_client_channel_join_requests(self, s, &appid) != 0) + if (handle_channel_join_requests(self, s, &appid) != 0) { return 1; } @@ -702,85 +702,6 @@ xrdp_mcs_send_aucf(struct xrdp_mcs *self) return 0; } -/*****************************************************************************/ -/* Processes a [ITU-T T.25] DomainMCSPDU with type ChannelJoinRequest - * - * Note: a parsing example can be found in [MS-RDPBCGR] 4.1.8.1.1 - * - * returns error */ -static int -xrdp_mcs_recv_cjrq(struct xrdp_mcs *self, int *channel_id) -{ - int opcode; - struct stream *s; - int initiator; - - s = libxrdp_force_read(self->iso_layer->trans); - if (s == 0) - { - LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.25] ChannelJoinRequest failed"); - return 1; - } - - if (xrdp_iso_recv(self->iso_layer, s) != 0) - { - LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.25] ChannelJoinRequest failed"); - return 1; - } - - if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] DomainMCSPDU")) - { - return 1; - } - - in_uint8(s, opcode); - LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] DomainMCSPDU " - "choice index %d (ChannelJoinRequest)", (opcode >> 2)); - - if ((opcode >> 2) != MCS_CJRQ) - { - LOG(LOG_LEVEL_ERROR, "Parsed [ITU-T T.125] DomainMCSPDU choice index " - "expected %d, received %d", MCS_CJRQ, (opcode >> 2)); - return 1; - } - - if (!s_check_rem_and_log(s, 4, "Parsing [ITU-T T.125] ChannelJoinRequest")) - { - return 1; - } - - in_uint16_be(s, initiator); - in_uint16_be(s, *channel_id); - - /* - * [MS-RDPBCGR] 2.2.1.8 says that the mcsAUrq field is 5 bytes (which have - * already been read into the opcode and other fields), so the nonStandard - * field should never be present. - */ - if (opcode & 2) - { - if (!s_check_rem_and_log(s, 2, "Parsing [ITU-T T.125] ChannelJoinRequest nonStandard")) - { - return 1; - } - in_uint8s(s, 2); /* NonStandardParameter.key - NonStandardParameter.data */ - } - - LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] ChannelJoinRequest " - "initiator %d, channelId %d, " - "nonStandard (%s)", - initiator, *channel_id, - (opcode & 2) ? "present" : "not present"); - - if (!s_check_end_and_log(s, "MCS protocol error [ITU-T T.125] ChannelJoinRequest")) - { - return 1; - } - - return 0; -} - /*****************************************************************************/ /* Write the identifier and length of a [ITU-T X.690] BER (Basic Encoding Rules) * structure header. @@ -873,6 +794,40 @@ xrdp_mcs_out_domain_params(struct xrdp_mcs *self, struct stream *s, max_channels, max_users, max_tokens, max_pdu_size); return 0; } + +/*****************************************************************************/ +/* + * Output a TS_UD_SC_CORE struct ([MS-RDPBCGR] 2.2.1.4.2) + */ +static void +out_server_core_data(struct xrdp_sec *self, struct stream *s) +{ + unsigned int version = 0x00080004; + unsigned int client_requested_protocols = 0; + unsigned int early_capability_flags = RNS_UD_SC_SKIP_CHANNELJOIN_SUPPORTED; + + if (self->mcs_layer->iso_layer->rdpNegData) + { + client_requested_protocols = + self->mcs_layer->iso_layer->requestedProtocol; + } + + /* [MS-RDPBCGR] TS_UD_HEADER */ + LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER " + "type 0x%4.4x, length %d", SEC_TAG_SRV_INFO, 16); + out_uint16_le(s, SEC_TAG_SRV_INFO); /* type */ + out_uint16_le(s, 16); /* length */ + + LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_CORE " + "version 0x%8.8x clientRequestedProtocols 0x%8.8x " + "earlyCapabilityFlags 0x%8.8x", + version, client_requested_protocols, + early_capability_flags); + out_uint32_le(s, version); + out_uint32_le(s, client_requested_protocols); + out_uint32_le(s, early_capability_flags); +} + /*****************************************************************************/ /* Write an [ITU-T T.124] ConnectData (ALIGNED variant of BASIC-PER) message * with ConnectGCCPDU, ConferenceCreateResponse, @@ -951,36 +906,7 @@ xrdp_mcs_out_gcc_data(struct xrdp_sec *self) out_uint8s(s, 2); ud_ptr = s->p; /* User Data */ - /* [MS-RDPBCGR] TS_UD_HEADER */ - out_uint16_le(s, SEC_TAG_SRV_INFO); /* type */ - if (self->mcs_layer->iso_layer->rdpNegData) - { - out_uint16_le(s, 12); /* length */ - } - else - { - out_uint16_le(s, 8); /* length */ - } - /* [MS-RDPBCGR] TS_UD_SC_CORE */ - out_uint8(s, 4); /* version (0x00080004 = rdp5, 0x00080001 = rdp4) */ - out_uint8(s, 0); - out_uint8(s, 8); - out_uint8(s, 0); /* version (last byte) */ - LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER " - "type 0x%4.4x, length %d", - SEC_TAG_SRV_INFO, - self->mcs_layer->iso_layer->rdpNegData ? 12 : 8); - LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_CORE " - " version 0x%8.8x", 0x00080004); - if (self->mcs_layer->iso_layer->rdpNegData) - { - /* RequestedProtocol */ - out_uint32_le(s, self->mcs_layer->iso_layer->requestedProtocol); /* clientRequestedProtocols */ - LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_CORE " - " clientRequestedProtocols 0x%8.8x", - self->mcs_layer->iso_layer->requestedProtocol); - } - + out_server_core_data(self, s); /* [MS-RDPBCGR] TS_UD_HEADER */ out_uint16_le(s, SEC_TAG_SRV_CHANNELS); /* type */ @@ -1172,16 +1098,13 @@ xrdp_mcs_send_connect_response(struct xrdp_mcs *self) } /*****************************************************************************/ -/* Handle all client channel join requests for a non-TLS connection +/* Handle all client channel join requests * * @param self MCS structure * @param s Input stream * @param[in,out] appid Type of the MCS PDU whose header has just been read. * @return 0 for success * - * For non-TLS connections, the channel join MCS PDUs are followed by - * another MCS PDU. This not the case for TLS connections. - * * Called when an MCS PDU header has been read, but the PDU has not * been processed. * @@ -1190,19 +1113,38 @@ xrdp_mcs_send_connect_response(struct xrdp_mcs *self) * the type of the next PDU is passed back to the caller for the caller * to process. * - * In order to cater for older clients which may not conform exactly to - * the specification, we simply take all the join requests which come in, - * and respond to them. + * We simply take all the join requests which come in, + * and respond to them. We do this for a number of reasons:- * - * See :- - * - https://github.com/neutrinolabs/xrdp/issues/2166 - * - [MS-RDPBCGR] 3.2.5.3.8 and 3.2.5.3.8 + * 1) Older clients may not issue exactly the documented number of + * channel join requests. See + * https://github.com/neutrinolabs/xrdp/issues/2166 + * 2) If we set RNS_UD_SC_SKIP_CHANNELJOIN_SUPPORTED in our early capabilities + * flags, and the client sets RNS_UD_CS_SUPPORT_SKIP_CHANNELJOIN, the + * client can still send channel join requests and be compliant with the + * spec. See [MS-RDPCCGR] 3.2.5.3.8. */ static int -handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self, - struct stream *s, int *appid) +handle_channel_join_requests(struct xrdp_mcs *self, + struct stream *s, int *appid) { int rv = 0; + unsigned int expected_join_count = 0; + if ((self->sec_layer->rdp_layer->client_info.mcs_early_capability_flags & + RNS_UD_CS_SUPPORT_SKIP_CHANNELJOIN) == 0) + { + /* + * The client has indicated it does not support skipping channel + * join request/confirm PDUs. + * + * Expect a channel join request PDU for each of the static + * virtual channels, plus the user channel (self->chanid) and + * the I/O channel (MCS_GLOBAL_CHANNEL) */ + expected_join_count = self->channel_list->count + 2; + } + + unsigned int actual_join_count = 0; + while (*appid == MCS_CJRQ) { int userid; @@ -1220,6 +1162,7 @@ handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self, LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] ChannelJoinRequest " "initiator 0x%4.4x, channelId 0x%4.4x", userid, chanid); + ++actual_join_count; if (xrdp_mcs_send_cjcf(self, userid, chanid) != 0) { @@ -1242,53 +1185,11 @@ handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self, } } - return rv; -} - -/*****************************************************************************/ -/* Handle all client channel join requests for a TLS connection - * - * @param self MCS structure - * @return 0 for success - * - * When were are about to negotiate a TLS connection, it is important that - * we agree on the exact number of client join request / client join confirm - * PDUs, so that we get the TLS 'client hello' message exactly when - * expected. - * - * See [MS-RDPBCGR] 3.2.5.3.8 and 3.2.5.3.8 - */ -static int -handle_tls_client_channel_join_requests(struct xrdp_mcs *self) -{ - int index; - int rv = 0; - - static const char *tag = "[MCS Connection Sequence (TLS)]"; - /* - * Expect a channel join request PDU for each of the static virtual - * channels, plus the user channel (self->chanid) and the I/O channel - * (MCS_GLOBAL_CHANNEL) */ - for (index = 0; index < self->channel_list->count + 2; index++) - { - int channel_id; - LOG(LOG_LEVEL_DEBUG, "%s receive channel join request", tag); - if (xrdp_mcs_recv_cjrq(self, &channel_id) != 0) - { - LOG(LOG_LEVEL_ERROR, "%s receive channel join request failed", tag); - rv = 1; - break; - } - - LOG(LOG_LEVEL_DEBUG, "%s send channel join confirm", tag); - if (xrdp_mcs_send_cjcf(self, self->userid, channel_id) != 0) - { - LOG(LOG_LEVEL_ERROR, "%s send channel join confirm failed", tag); - rv = 1; - break; - } + if (rv == 0 && expected_join_count != actual_join_count) + { + LOG(LOG_LEVEL_WARNING, "Expected %u channel join PDUs but got %u", + expected_join_count, actual_join_count); } - return rv; } @@ -1350,24 +1251,9 @@ xrdp_mcs_incoming(struct xrdp_mcs *self) return 1; } - if (self->iso_layer->selectedProtocol > PROTOCOL_RDP) - { - /* TLS connection. Client and server have to agree on MCS channel - * join messages, and these have to be processed before the TLS - * client hello */ - if (handle_tls_client_channel_join_requests(self) != 0) - { - return 1; - } - - LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence (TLS)] completed"); - } - else - { - /* Non-TLS connection - channel joins handled in MCS PDU - * processing loop */ - self->expecting_channel_join_requests = 1; - } + /* Tell the MCS PDU processing loop that (zere or more ) channel + join requests are expected at this point */ + self->expecting_channel_join_requests = 1; return 0; } diff --git a/mc/Makefile.am b/mc/Makefile.am index 9ed72135ac..b36c40e9e2 100644 --- a/mc/Makefile.am +++ b/mc/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/neutrinordp/Makefile.am b/neutrinordp/Makefile.am index d9c31bc325..75cd0adf38 100644 --- a/neutrinordp/Makefile.am +++ b/neutrinordp/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/neutrinordp/xrdp-neutrinordp.c b/neutrinordp/xrdp-neutrinordp.c index b33bd04290..83a2214c2f 100644 --- a/neutrinordp/xrdp-neutrinordp.c +++ b/neutrinordp/xrdp-neutrinordp.c @@ -1790,6 +1790,10 @@ lfreerdp_synchronize(rdpContext *context) static boolean lfreerdp_pre_connect(freerdp *instance) { +#define MAX_FREERDP_CHANNELS \ + (sizeof(instance->settings->channels) / \ + sizeof(instance->settings->channels[0])) + struct mod *mod; int index; int error; @@ -1797,7 +1801,7 @@ lfreerdp_pre_connect(freerdp *instance) int target_chan; int ch_flags; char ch_name[256]; - const char *ch_names[MAX_STATIC_CHANNELS]; + const char *ch_names[MAX_FREERDP_CHANNELS]; char *dst_ch_name; LOG_DEVEL(LOG_LEVEL_INFO, "lfreerdp_pre_connect:"); @@ -1828,7 +1832,7 @@ lfreerdp_pre_connect(freerdp *instance) LOG(LOG_LEVEL_INFO, "Channel '%s' not passed to module", ch_name); } - else if (target_chan < MAX_STATIC_CHANNELS) + else if (target_chan < MAX_FREERDP_CHANNELS) { dst_ch_name = instance->settings->channels[target_chan].name; ch_names[target_chan] = dst_ch_name; diff --git a/neutrinordp/xrdp-neutrinordp.h b/neutrinordp/xrdp-neutrinordp.h index a616dca317..fc8aa7ae7c 100644 --- a/neutrinordp/xrdp-neutrinordp.h +++ b/neutrinordp/xrdp-neutrinordp.h @@ -231,7 +231,7 @@ struct mod struct bitmap_item bitmap_cache[4][4096]; struct brush_item brush_cache[64]; struct pointer_item pointer_cache[32]; - char pamusername[255]; + char pamusername[256]; int allow_client_experiencesettings; int perf_settings_override_mask; /* Performance bits overridden in ini file */ diff --git a/scripts/install_cppcheck.sh b/scripts/install_cppcheck.sh index 5dd45e804d..16a18e5d59 100755 --- a/scripts/install_cppcheck.sh +++ b/scripts/install_cppcheck.sh @@ -149,9 +149,13 @@ fi # Cppcheck 2.8 added optional support for utilizing Boost make_args="$make_args CPPFLAGS=-DHAVE_BOOST" ;; - 2.*) + 2.15.*) # Cppcheck 2.15 doesn't seem to define FILESDIR if CPPFLAGS is set ;; + 2.*) + # Cppcheck 2.16 (and later) fixes the HAVE_BOOST issue + make_args="$make_args CPPFLAGS=-DHAVE_BOOST" + ;; esac # Use all available CPUs diff --git a/scripts/install_xrdp_build_dependencies_with_apt.sh b/scripts/install_xrdp_build_dependencies_with_apt.sh index d41be898d5..246dd84497 100755 --- a/scripts/install_xrdp_build_dependencies_with_apt.sh +++ b/scripts/install_xrdp_build_dependencies_with_apt.sh @@ -90,7 +90,7 @@ in PACKAGES="$PACKAGES \ $PACKAGES_AMD64_MIN \ $LIBFREETYPE_DEV \ - libfuse-dev \ + libfuse3-dev \ libjpeg-dev \ libmp3lame-dev \ libfdk-aac-dev \ @@ -98,7 +98,8 @@ in libimlib2-dev \ libopus-dev \ libpixman-1-dev \ - libx264-dev" + libx264-dev \ + libopenh264-dev" ;; *) echo "unsupported feature set: $FEATURE_SET" @@ -115,15 +116,15 @@ in # build support tool. # - Ubuntu 18.04 -> 20.04 # Removed fdk-aac-dev:i386 and libfuse-dev:i386 + # - Ubuntu 24.04.1 + # Removed libibus-1.0-dev:i386 and libimlib2-dev:i386 PACKAGES="$PACKAGES \ g++-multilib \ gcc-multilib \ $LIBFREETYPE_DEV:i386 \ libgl1-mesa-dev:i386 \ libglu1-mesa-dev:i386 \ - libibus-1.0-dev:i386 \ libjpeg-dev:i386 \ - libimlib2-dev:i386 \ libmp3lame-dev:i386 \ libopus-dev:i386 \ libpam0g-dev:i386 \ diff --git a/sesman/Makefile.am b/sesman/Makefile.am index 8d9d31022c..9ddef8e84e 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -2,7 +2,7 @@ EXTRA_DIST = \ Doxyfile AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_LIBEXEC_PATH=\"${libexecdir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ @@ -40,7 +40,7 @@ xrdp_sesman_LDADD = \ $(top_builddir)/libipm/libipm.la \ $(top_builddir)/common/libcommon.la -sesmansysconfdir=$(sysconfdir)/xrdp +sesmansysconfdir=$(sysconfdir)/$(sysconfsubdir) SUBST_VARS = sed \ -e 's|@sesmansysconfdir[@]|$(sesmansysconfdir)|g' diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index 4b0154fac1..e53961b440 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -4,18 +4,19 @@ EXTRA_DIST = \ wave-format-server.txt AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ -DXRDP_SOCKET_ROOT_PATH=\"${socketdir}\" \ -I$(top_srcdir)/sesman/libsesman \ - -I$(top_srcdir)/common + -I$(top_srcdir)/common \ + -I$(top_srcdir)/libipm CHANSRV_EXTRA_LIBS = if XRDP_FUSE -AM_CPPFLAGS += -DXRDP_FUSE $(FUSE_CFLAGS) -DFUSE_USE_VERSION=26 +AM_CPPFLAGS += -DXRDP_FUSE $(FUSE_CFLAGS) -DFUSE_USE_VERSION=30 CHANSRV_EXTRA_LIBS += $(FUSE_LIBS) endif diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 2cd8cf0ae7..495d270c37 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -45,6 +45,9 @@ #include "xrdp_sockets.h" #include "audin.h" +#include "scp.h" +#include "scp_sync.h" + #include "ms-rdpbcgr.h" #define MAX_PATH 260 @@ -129,7 +132,7 @@ add_timeout(int msoffset, void (*callback)(void *data), void *data) tui32 now; LOG_DEVEL(LOG_LEVEL_DEBUG, "add_timeout:"); - now = g_time3(); + now = g_get_elapsed_ms(); tobj = g_new0(struct timeout_obj, 1); tobj->mstime = now + msoffset; tobj->callback = callback; @@ -164,7 +167,7 @@ get_timeout(int *timeout) tobj = g_timeout_head; if (tobj != 0) { - now = g_time3(); + now = g_get_elapsed_ms(); while (tobj != 0) { LOG_DEVEL(LOG_LEVEL_DEBUG, " now %u tobj->mstime %u", now, tobj->mstime); @@ -212,7 +215,7 @@ check_timeout(void) while (tobj != 0) { count++; - now = g_time3(); + now = g_get_elapsed_ms(); if (now >= tobj->mstime) { tobj->callback(tobj->data); @@ -1691,20 +1694,32 @@ get_log_path(char *path, int bytes) int rv; rv = 1; - log_path = g_getenv("CHANSRV_LOG_PATH"); - if (log_path == 0) + if (g_cfg->log_file_path != NULL && g_cfg->log_file_path[0] != '\0') { - log_path = g_getenv("XDG_DATA_HOME"); - if (log_path != 0) + char uidstr[64]; + char username[64]; + const struct info_string_tag map[] = { - g_snprintf(path, bytes, "%s%s", log_path, "/xrdp"); - if (g_directory_exist(path) || (g_mkdir(path) == 0)) - { - rv = 0; - } + {'u', uidstr}, + {'U', username}, + INFO_STRING_END_OF_LIST + }; + + int uid = g_getuid(); + g_snprintf(uidstr, sizeof(uidstr), "%d", uid); + if (g_getlogin(username, sizeof(username)) != 0) + { + /* Fall back to UID */ + g_strncpy(username, uidstr, sizeof(username) - 1); + } + + (void)g_format_info_string(path, bytes, g_cfg->log_file_path, map); + if (g_directory_exist(path) || (g_mkdir(path) == 0)) + { + rv = 0; } } - else + else if ((log_path = g_getenv("CHANSRV_LOG_PATH")) != 0) { g_snprintf(path, bytes, "%s", log_path); if (g_directory_exist(path) || (g_mkdir(path) == 0)) @@ -1712,6 +1727,16 @@ get_log_path(char *path, int bytes) rv = 0; } } + else if ((log_path = g_getenv("XDG_DATA_HOME")) != 0) + { + g_snprintf(path, bytes, "%s%s", log_path, "/xrdp"); + if (g_directory_exist(path) || (g_mkdir(path) == 0)) + { + rv = 0; + } + } + + // Always fall back to the home directory if (rv != 0) { log_path = g_getenv("HOME"); @@ -1763,6 +1788,54 @@ run_exec(void) return 0; } +/*****************************************************************************/ +/** + * Make sure XRDP_SOCKET_PATH exists + * + * We can't do anything without XRDP_SOCKET_PATH existing. + * + * Normally this is done by sesman before chansrv starts. If we're running + * standalone however (i.e. with x11vnc) this won't be done. We don't have the + * privilege to create the directory, so we have to ask sesman to do it + * for us. + */ +static int +chansrv_create_xrdp_socket_path(void) +{ + char xrdp_socket_path[XRDP_SOCKETS_MAXPATH]; + int rv = 1; + + /* Use our UID to qualify XRDP_SOCKET_PATH */ + g_snprintf(xrdp_socket_path, sizeof(xrdp_socket_path), + XRDP_SOCKET_PATH, g_getuid()); + + if (g_directory_exist(xrdp_socket_path)) + { + rv = 0; + } + else + { + LOG(LOG_LEVEL_INFO, "%s doesn't exist - asking sesman to create it", + xrdp_socket_path); + + struct trans *t = NULL; + + if (!(t = scp_connect(g_cfg->listen_port, "xrdp-chansrv", g_is_term))) + { + LOG(LOG_LEVEL_ERROR, "Can't connect to sesman"); + } + else if (scp_sync_uds_login_request(t) == 0 && + scp_sync_create_sockdir_request(t) == 0) + { + rv = 0; + (void)scp_send_close_connection_request(t); + } + trans_delete(t); + } + + return rv; +} + /*****************************************************************************/ int main(int argc, char **argv) @@ -1779,14 +1852,6 @@ main(int argc, char **argv) g_init("xrdp-chansrv"); /* os_calls */ g_memset(g_drdynvcs, 0, sizeof(g_drdynvcs)); - log_path[255] = 0; - if (get_log_path(log_path, 255) != 0) - { - g_writeln("error reading CHANSRV_LOG_PATH and HOME environment variable"); - main_cleanup(); - return 1; - } - display_text = g_getenv("DISPLAY"); if (display_text == NULL) { @@ -1813,6 +1878,13 @@ main(int argc, char **argv) } config_dump(g_cfg); + if (get_log_path(log_path, sizeof(log_path)) != 0) + { + g_writeln("error reading CHANSRV_LOG_PATH and HOME environment variable"); + main_cleanup(); + return 1; + } + pid = g_getpid(); /* starting logging subsystem */ @@ -1855,6 +1927,13 @@ main(int argc, char **argv) } LOG_DEVEL(LOG_LEVEL_INFO, "main: app started pid %d(0x%8.8x)", pid, pid); + + if (chansrv_create_xrdp_socket_path() != 0) + { + main_cleanup(); + return 1; + } + /* set up signal handler */ g_signal_terminate(term_signal_handler); /* SIGTERM */ g_signal_user_interrupt(term_signal_handler); /* SIGINT */ diff --git a/sesman/chansrv/chansrv_config.c b/sesman/chansrv/chansrv_config.c index 000c8a4c8e..14bd43b291 100644 --- a/sesman/chansrv/chansrv_config.c +++ b/sesman/chansrv/chansrv_config.c @@ -40,12 +40,14 @@ #define DEFAULT_RESTRICT_INBOUND_CLIPBOARD 0 #define DEFAULT_ENABLE_FUSE_MOUNT 1 #define DEFAULT_FUSE_MOUNT_NAME "xrdp-client" +#define DEFAULT_FUSE_MOUNT_NAME_COLON_CHAR_REPLACEMENT ':' #define DEFAULT_FUSE_DIRECT_IO 0 #define DEFAULT_FILE_UMASK 077 #define DEFAULT_USE_NAUTILUS3_FLIST_FORMAT 0 #define DEFAULT_NUM_SILENT_FRAMES_AAC 4 #define DEFAULT_NUM_SILENT_FRAMES_MP3 2 #define DEFAULT_MSEC_DO_NOT_SEND 1000 +#define DEFAULT_LOG_FILE_PATH "" /** * Type used for passing a logging function about */ @@ -73,6 +75,61 @@ log_to_stdout(const enum logLevels lvl, const char *msg, ...) return LOG_STARTUP_OK; } +/***************************************************************************//** + * Reads the config values we need from the [Globals] section + * + * @param logmsg Function to use to log messages + * @param names List of definitions in the section + * @params values List of corresponding values for the names + * @params cfg Pointer to structure we're filling in + * + * @return 0 for success + */ +static int +read_config_globals(log_func_t logmsg, + struct list *names, struct list *values, + struct config_chansrv *cfg) +{ + int error = 0; + int index; + + for (index = 0; index < names->count; ++index) + { + const char *name = (const char *)list_get_item(names, index); + const char *value = (const char *)list_get_item(values, index); + + char unrecognised[256]; + if (g_strcasecmp(name, "ListenPort") == 0) + { + char *listen_port = strdup(value); + if (listen_port == NULL) + { + LOG(LOG_LEVEL_WARNING, + "Can't allocate config memory for ListenPort"); + } + else + { + g_free(cfg->listen_port); + cfg->listen_port = listen_port; + } + } + if (g_strcasecmp(name, "RestrictInboundClipboard") == 0) + { + cfg->restrict_inbound_clipboard = + sesman_clip_restrict_string_to_bitmask( + value, unrecognised, sizeof(unrecognised)); + if (unrecognised[0] != '\0') + { + LOG(LOG_LEVEL_WARNING, + "Unrecognised tokens parsing 'RestrictInboundClipboard' %s", + unrecognised); + } + } + } + + return error; +} + /***************************************************************************//** * Reads the config values we need from the [Security] section * @@ -164,6 +221,25 @@ read_config_chansrv(log_func_t logmsg, break; } } + else if (g_strcasecmp(name, "FuseMountNameColonCharReplacement") == 0) + { + size_t vallen = g_strlen(value); + if (vallen < 1) + { + cfg->fuse_mount_name_colon_char_replacement = '\0'; + } + else + { + if (vallen > 1) + { + logmsg(LOG_LEVEL_WARNING, "FuseMountNameColonCharReplacement " + "must be 1 character length, now it is '%s'." + "Only first char will be used!", + value); + } + cfg->fuse_mount_name_colon_char_replacement = value[0]; + } + } else if (g_strcasecmp(name, "FuseDirectIO") == 0) { cfg->fuse_direct_io = g_text2bool(value); @@ -193,6 +269,45 @@ read_config_chansrv(log_func_t logmsg, return error; } +/***************************************************************************//** + * Reads the config values we need from the [ChansrvLogging] section + * + * @param logmsg Function to use to log messages + * @param names List of definitions in the section + * @params values List of corresponding values for the names + * @params cfg Pointer to structure we're filling in + * + * @return 0 for success + */ +static int +read_config_chansrv_logging(log_func_t logmsg, + struct list *names, struct list *values, + struct config_chansrv *cfg) +{ + int error = 0; + int index; + + for (index = 0; index < names->count; ++index) + { + const char *name = (const char *)list_get_item(names, index); + const char *value = (const char *)list_get_item(values, index); + + if (g_strcasecmp(name, "LogFilePath") == 0) + { + g_free(cfg->log_file_path); + cfg->log_file_path = g_strdup(value); + if (cfg->log_file_path == NULL) + { + logmsg(LOG_LEVEL_ERROR, "Can't alloc LogFilePath"); + error = 1; + break; + } + } + } + + return error; +} + /***************************************************************************//** * @brief returns a config block with default values * @@ -204,25 +319,30 @@ new_config(void) /* Do all the allocations at the beginning, then check them together */ struct config_chansrv *cfg = g_new0(struct config_chansrv, 1); char *fuse_mount_name = g_strdup(DEFAULT_FUSE_MOUNT_NAME); - if (cfg == NULL || fuse_mount_name == NULL) + char *log_file_path = g_strdup(DEFAULT_LOG_FILE_PATH); + if (cfg == NULL || fuse_mount_name == NULL || log_file_path == NULL) { /* At least one memory allocation failed */ + g_free(log_file_path); g_free(fuse_mount_name); g_free(cfg); cfg = NULL; } else { + cfg->listen_port = NULL; cfg->enable_fuse_mount = DEFAULT_ENABLE_FUSE_MOUNT; cfg->restrict_outbound_clipboard = DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD; cfg->restrict_inbound_clipboard = DEFAULT_RESTRICT_INBOUND_CLIPBOARD; cfg->fuse_mount_name = fuse_mount_name; + cfg->fuse_mount_name_colon_char_replacement = DEFAULT_FUSE_MOUNT_NAME_COLON_CHAR_REPLACEMENT; cfg->fuse_direct_io = DEFAULT_FUSE_DIRECT_IO; cfg->file_umask = DEFAULT_FILE_UMASK; cfg->use_nautilus3_flist_format = DEFAULT_USE_NAUTILUS3_FLIST_FORMAT; cfg->num_silent_frames_aac = DEFAULT_NUM_SILENT_FRAMES_AAC; cfg->num_silent_frames_mp3 = DEFAULT_NUM_SILENT_FRAMES_MP3; cfg->msec_do_not_send = DEFAULT_MSEC_DO_NOT_SEND; + cfg->log_file_path = log_file_path; } return cfg; @@ -258,6 +378,11 @@ config_read(int use_logger, const char *sesman_ini) names->auto_free = 1; values->auto_free = 1; + if (!error && file_read_section(fd, "Globals", names, values) == 0) + { + error = read_config_globals(logmsg, names, values, cfg); + } + if (!error && file_read_section(fd, "Security", names, values) == 0) { error = read_config_security(logmsg, names, values, cfg); @@ -268,6 +393,12 @@ config_read(int use_logger, const char *sesman_ini) error = read_config_chansrv(logmsg, names, values, cfg); } + if (!error && + file_read_section(fd, "ChansrvLogging", names, values) == 0) + { + error = read_config_chansrv_logging(logmsg, names, values, cfg); + } + list_delete(names); list_delete(values); } @@ -288,9 +419,12 @@ config_read(int use_logger, const char *sesman_ini) void config_dump(struct config_chansrv *config) { + char buf[256]; + g_writeln("Global configuration:"); + g_writeln(" xrdp-sesman ListenPort: %s", + (config->listen_port) ? config->listen_port : ""); - char buf[256]; g_writeln("\nSecurity configuration:"); sesman_clip_restrict_mask_to_string( config->restrict_outbound_clipboard, @@ -306,11 +440,14 @@ config_dump(struct config_chansrv *config) g_writeln(" EnableFuseMount %s", g_bool2text(config->enable_fuse_mount)); g_writeln(" FuseMountName: %s", config->fuse_mount_name); + g_writeln(" FuseMountNameColonCharReplacement: %c", config->fuse_mount_name_colon_char_replacement); g_writeln(" FuseDirectIO: %s", g_bool2text(config->fuse_direct_io)); g_writeln(" FileMask: 0%o", config->file_umask); g_writeln(" Nautilus 3 Flist Format: %s", g_bool2text(config->use_nautilus3_flist_format)); + g_writeln(" LogFilePath : %s", + (config->log_file_path[0]) ? config->log_file_path : ""); } /******************************************************************************/ @@ -319,7 +456,9 @@ config_free(struct config_chansrv *cc) { if (cc != NULL) { + g_free(cc->listen_port); g_free(cc->fuse_mount_name); + g_free(cc->log_file_path); g_free(cc); } } diff --git a/sesman/chansrv/chansrv_config.h b/sesman/chansrv/chansrv_config.h index a7b30bc694..031bd487ec 100644 --- a/sesman/chansrv/chansrv_config.h +++ b/sesman/chansrv/chansrv_config.h @@ -23,6 +23,9 @@ struct config_chansrv { + /** sesman listening port */ + char *listen_port; + /** Whether the FUSE mount is enabled or not */ int enable_fuse_mount; @@ -36,6 +39,8 @@ struct config_chansrv /** * FuseMountName from sesman.ini */ char *fuse_mount_name; + /** * FuseMountNameColonCharReplacement from sesman.ini */ + char fuse_mount_name_colon_char_replacement; /** FileUmask from sesman.ini */ mode_t file_umask; @@ -47,6 +52,9 @@ struct config_chansrv unsigned int num_silent_frames_mp3; /** Do net send sound data afer SNDC_CLOSE is sent. unit is millisecond, setting from sesman.ini */ unsigned int msec_do_not_send; + + /** LogFilePath from sesman.ini ([ChansrvLogging]) */ + char *log_file_path; }; diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index 64f6364e73..579a1c8461 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -127,6 +127,11 @@ void xfuse_devredir_cb_rename_file(struct state_rename *fip, void xfuse_devredir_cb_file_close(struct state_close *fip) {} +void xfuse_devredir_cb_statfs(struct state_statfs *fip, + const struct statvfs *fss, + enum NTSTATUS IoStatus) +{} + int xfuse_path_in_xfuse_fs(const char *path) { return 0; @@ -169,6 +174,15 @@ int xfuse_path_in_xfuse_fs(const char *path) #include "list.h" #include "file.h" +/* Check for FUSE features we may wish to use + * + * Note that FUSE_VERSION might be more useful for some features than + * FUSE_USE_VERSION + */ +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3,7) +#define FUSE_SET_LOG_FUNC_AVAILABLE +#endif + #ifndef EREMOTEIO #define EREMOTEIO EIO #endif @@ -177,6 +191,36 @@ int xfuse_path_in_xfuse_fs(const char *path) #define XFUSE_ENTRY_TIMEOUT 5.0 +extern struct config_chansrv *g_cfg; /* in chansrv.c */ + + +/* Local utility functions */ + +static inline char * +_fuse_mount_name_colon_char_replace(const char *dirname) +{ + char *newdirname = (char *) dirname; + if (g_cfg->fuse_mount_name_colon_char_replacement != ':') + { + newdirname = g_strdup(dirname); + if (newdirname == NULL) + { + LOG_DEVEL(LOG_LEVEL_ERROR, + "Failed to duplicate fuse mount name string"); + return (char *) dirname; + } + char *colonptr = g_strrchr(newdirname, ':'); + if (colonptr != NULL) + { + *colonptr = g_cfg->fuse_mount_name_colon_char_replacement; + } + } + return newdirname; +} + +/* End of Local utility functions*/ + + /* Type of buffer used for fuse_add_direntry() calls */ struct dirbuf1 { @@ -294,6 +338,15 @@ struct state_close fuse_ino_t inum; /* inum of file to open */ }; +/* + * Record type used to maintain state when running a statfs + */ +struct state_statfs +{ + fuse_req_t req; /* Original FUSE request from statfs */ +}; + + struct xfuse_handle { tui32 DeviceId; @@ -320,20 +373,18 @@ struct req_list_item int size; }; -extern struct config_chansrv *g_cfg; /* in chansrv.c */ static struct list *g_req_list = 0; static struct xfs_fs *g_xfs; /* an inst of xrdp file system */ static ino_t g_clipboard_inum; /* inode of clipboard dir */ -static char *g_mount_point = 0; /* our FUSE mount point */ static struct fuse_lowlevel_ops g_xfuse_ops; /* setup FUSE callbacks */ static int g_xfuse_inited = 0; /* true when FUSE is inited */ -static struct fuse_chan *g_ch = 0; static struct fuse_session *g_se = 0; -static char *g_buffer = 0; -static int g_fd = 0; -static tintptr g_bufsize = 0; - +// For the below, see the source for the fuse_session_loop() function +static struct fuse_buf g_buffer = +{ + .mem = NULL +}; /* forward declarations for internal access */ static int xfuse_init_xrdp_fs(void); @@ -362,7 +413,8 @@ static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, static void xfuse_cb_rename(fuse_req_t req, fuse_ino_t old_parent, const char *old_name, - fuse_ino_t new_parent, const char *new_name); + fuse_ino_t new_parent, const char *new_name, + unsigned int flags); /* Whether to create a dir of file depends on whether S_IFDIR is set in the mode field */ @@ -400,6 +452,8 @@ static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino, static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); +static void xfuse_cb_statfs(fuse_req_t req, fuse_ino_t ino); + /* miscellaneous functions */ static void xfs_inode_to_fuse_entry_param(const XFS_INODE *xinode, struct fuse_entry_param *e); @@ -496,9 +550,9 @@ xfuse_init(void) return 1; } - if (g_ch != 0) + if (g_se != 0) { - LOG_DEVEL(LOG_LEVEL_ERROR, "g_ch is not zero"); + LOG_DEVEL(LOG_LEVEL_ERROR, "g_se is not zero"); return -1; } @@ -602,17 +656,21 @@ xfuse_init(void) g_xfuse_ops.setattr = xfuse_cb_setattr; g_xfuse_ops.opendir = xfuse_cb_opendir; g_xfuse_ops.releasedir = xfuse_cb_releasedir; + g_xfuse_ops.statfs = xfuse_cb_statfs; fuse_opt_add_arg(&args, "xrdp-chansrv"); - fuse_opt_add_arg(&args, g_fuse_root_path); + fuse_opt_add_arg(&args, "-o"); + fuse_opt_add_arg(&args, "fsname=xrdp-chansrv"); //fuse_opt_add_arg(&args, "-s"); /* single threaded mode */ //fuse_opt_add_arg(&args, "-d"); /* debug mode */ if (xfuse_init_lib(&args)) { + fuse_opt_free_args(&args); xfuse_deinit(); return -1; } + fuse_opt_free_args(&args); g_xfuse_inited = 1; return 0; @@ -627,30 +685,18 @@ xfuse_init(void) int xfuse_deinit(void) { - if (g_ch != 0) - { - fuse_session_remove_chan(g_ch); - fuse_unmount(g_mount_point, g_ch); - g_ch = 0; - } - - if (g_se != 0) + if (g_se != NULL) { + fuse_session_unmount(g_se); fuse_session_destroy(g_se); - g_se = 0; + g_se = NULL; } - if (g_buffer != 0) - { - g_free(g_buffer); - g_buffer = 0; - } + free(g_buffer.mem); + g_buffer.mem = NULL; - if (g_req_list != 0) - { - list_delete(g_req_list); - g_req_list = 0; - } + list_delete(g_req_list); + g_req_list = 0; xfuse_deinit_xrdp_fs(); @@ -665,35 +711,28 @@ xfuse_deinit(void) *****************************************************************************/ int xfuse_check_wait_objs(void) { - struct fuse_chan *tmpch; - int rval; - - if (g_ch == 0) + if (g_se != NULL) { - return 0; - } - - if (g_sck_can_recv(g_fd, 0)) - { - tmpch = g_ch; - - rval = fuse_chan_recv(&tmpch, g_buffer, g_bufsize); - if (rval == -EINTR) + if (g_sck_can_recv(fuse_session_fd(g_se), 0)) { - return -1; - } + int rval = fuse_session_receive_buf(g_se, &g_buffer); + if (rval == -EINTR) + { + return -1; + } - if (rval == -ENODEV) - { - return -1; - } + if (rval == -ENODEV) + { + return -1; + } - if (rval <= 0) - { - return -1; - } + if (rval <= 0) + { + return -1; + } - fuse_session_process(g_se, g_buffer, rval, tmpch); + fuse_session_process_buf(g_se, &g_buffer); + } } return 0; @@ -707,18 +746,12 @@ int xfuse_check_wait_objs(void) int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) { - int lcount; - - if (g_ch == 0) + if (g_se != NULL) { - return 0; + objs[*count] = fuse_session_fd(g_se); + ++(*count); } - lcount = *count; - objs[lcount] = g_fd; - lcount++; - *count = lcount; - return 0; } @@ -738,7 +771,13 @@ int xfuse_create_share(tui32 device_id, const char *dirname) if (dirname != NULL && strlen(dirname) > 0 && xfuse_init_xrdp_fs() == 0) { - xinode = xfs_add_entry(g_xfs, FUSE_ROOT_ID, dirname, (0777 | S_IFDIR)); + char *newdirname = _fuse_mount_name_colon_char_replace(dirname); + xinode = xfs_add_entry(g_xfs, FUSE_ROOT_ID, newdirname, (0777 | S_IFDIR)); + //free only if _fuse_mount_name_colon_char_replace allocated new string + if (newdirname != dirname) + { + g_free(newdirname); + } if (xinode == NULL) { LOG_DEVEL(LOG_LEVEL_DEBUG, "xfs_add_entry() failed"); @@ -894,51 +933,84 @@ int xfuse_file_contents_size(int stream_id, int file_size) ** ** *****************************************************************************/ +/*****************************************************************************/ +/** + * FUSE logging function + * + * Used to get errors from FUSE for the log file + * @param fuse_log_level Logging level understood by FUSE + * @param fmt Logging format string + * @param ap Arguments for above + */ +#ifdef FUSE_SET_LOG_FUNC_AVAILABLE +static void +xfuse_log_func(enum fuse_log_level fuse_log_level, const char *fmt, va_list ap) +{ + char msg[512]; + enum logLevels level; + switch (fuse_log_level) + { + case FUSE_LOG_ERR: + level = LOG_LEVEL_ERROR; + break; + + case FUSE_LOG_WARNING: + level = LOG_LEVEL_WARNING; + break; + + case FUSE_LOG_NOTICE: + case FUSE_LOG_INFO: + level = LOG_LEVEL_INFO; + break; + + case FUSE_LOG_DEBUG: + level = LOG_LEVEL_DEBUG; + break; + + default: + level = LOG_LEVEL_ALWAYS; + break; + } + + vsnprintf(msg, sizeof(msg), fmt, ap); + LOG(level, "%s", msg); +} +#endif //FUSE_SET_LOG_FUNC_AVAILABLE + /** * Initialize FUSE library * * @return 0 on success, -1 on failure *****************************************************************************/ - static int xfuse_init_lib(struct fuse_args *args) { - if (fuse_parse_cmdline(args, &g_mount_point, 0, 0) < 0) + int rv = -1; + +#ifdef FUSE_SET_LOG_FUNC_AVAILABLE + fuse_set_log_func(xfuse_log_func); +#endif + + g_se = fuse_session_new(args, &g_xfuse_ops, sizeof(g_xfuse_ops), 0); + if (g_se == NULL) { - LOG(LOG_LEVEL_ERROR, "fuse_parse_cmdline() failed"); - fuse_opt_free_args(args); - return -1; + LOG(LOG_LEVEL_ERROR, "fuse_session_new() failed"); } - - if ((g_ch = fuse_mount(g_mount_point, args)) == 0) + else if (fuse_session_mount(g_se, g_fuse_root_path) != 0) { LOG(LOG_LEVEL_ERROR, "FUSE mount on %s failed." " If %s is already mounted, you must first unmount it", - g_mount_point, g_mount_point); - fuse_opt_free_args(args); - return -1; + g_fuse_root_path, g_fuse_root_path); + fuse_session_destroy(g_se); + g_se = NULL; } - - g_se = fuse_lowlevel_new(args, &g_xfuse_ops, sizeof(g_xfuse_ops), 0); - if (g_se == 0) + else { - LOG(LOG_LEVEL_ERROR, "fuse_lowlevel_new() failed"); - fuse_unmount(g_mount_point, g_ch); - g_ch = 0; - fuse_opt_free_args(args); - return -1; + g_req_list = list_create(); + g_req_list->auto_free = 1; + rv = 0; } - fuse_opt_free_args(args); - fuse_session_add_chan(g_se, g_ch); - g_bufsize = fuse_chan_bufsize(g_ch); - - g_buffer = g_new0(char, g_bufsize); - g_fd = fuse_chan_fd(g_ch); - - g_req_list = list_create(); - g_req_list->auto_free = 1; - - return 0; + return rv; } /** @@ -1097,16 +1169,26 @@ void xfuse_devredir_cb_enum_dir_done(struct state_dirscan *fip, struct fuse_file_info *fi = &fip->fi; XFUSE_HANDLE *xhandle = xfuse_handle_create(); - if (xhandle == NULL - || (xhandle->dir_handle = xfs_opendir(g_xfs, fip->pinum)) == NULL) + if (xhandle == NULL) { - xfuse_handle_delete(xhandle); fuse_reply_err(fip->req, ENOMEM); } else { - fi->fh = xfuse_handle_to_fuse_handle(xhandle); - fuse_reply_open(fip->req, &fip->fi); + // Coverity gets confused by xfuse_handle_to_fuse_handle(), and + // sees the dir_handle leaked + //coverity[RESOURCE_LEAK:FALSE] + xhandle->dir_handle = xfs_opendir(g_xfs, fip->pinum); + if (xhandle->dir_handle == NULL) + { + xfuse_handle_delete(xhandle); + fuse_reply_err(fip->req, ENOMEM); + } + else + { + fi->fh = xfuse_handle_to_fuse_handle(xhandle); + fuse_reply_open(fip->req, &fip->fi); + } } } @@ -1503,7 +1585,10 @@ void xfuse_devredir_cb_rmdir_or_file(struct state_remove *fip, { case STATUS_SUCCESS: case STATUS_NO_SUCH_FILE: - xfs_remove_entry(g_xfs, xinode->inum); /* Remove local copy */ + if (xinode != NULL) + { + xfs_remove_entry(g_xfs, xinode->inum); /* Remove local copy */ + } fuse_reply_err(fip->req, 0); break; @@ -1550,6 +1635,26 @@ void xfuse_devredir_cb_file_close(struct state_close *fip) free(fip); } +void xfuse_devredir_cb_statfs(struct state_statfs *fip, + const struct statvfs *fss, + enum NTSTATUS IoStatus) +{ + int status; + if (IoStatus != STATUS_SUCCESS) + { + status = + (IoStatus == STATUS_ACCESS_DENIED) ? EACCES : + /* default */ EIO ; + fuse_reply_err(fip->req, status); + } + else + { + fuse_reply_statfs(fip->req, fss); + } + + free(fip); +} + /* * Determine is a file is in the FUSE filesystem * @@ -1934,7 +2039,8 @@ static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, static void xfuse_cb_rename(fuse_req_t req, fuse_ino_t old_parent, const char *old_name, - fuse_ino_t new_parent, const char *new_name) + fuse_ino_t new_parent, const char *new_name, + unsigned int flags) { XFS_INODE *old_xinode; XFS_INODE *new_parent_xinode; @@ -1942,8 +2048,13 @@ static void xfuse_cb_rename(fuse_req_t req, LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: old_parent=%ld old_name=%s new_parent=%ld new_name=%s", old_parent, old_name, new_parent, new_name); - if (strlen(old_name) > XFS_MAXFILENAMELEN || - strlen(new_name) > XFS_MAXFILENAMELEN) + // renameat2() flags (in stdio.h) are not supported + if (flags != 0) + { + fuse_reply_err(req, EINVAL); + } + else if (strlen(old_name) > XFS_MAXFILENAMELEN || + strlen(new_name) > XFS_MAXFILENAMELEN) { fuse_reply_err(req, ENAMETOOLONG); } @@ -2589,7 +2700,11 @@ static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino, } else { - if ((xhandle->dir_handle = xfs_opendir(g_xfs, ino)) == NULL) + // Coverity gets confused by xfuse_handle_to_fuse_handle(), and + // sees the dir_handle leaked + //coverity[RESOURCE_LEAK:FALSE] + xhandle->dir_handle = xfs_opendir(g_xfs, ino); + if (xhandle->dir_handle == NULL) { xfuse_handle_delete(xhandle); fuse_reply_err(req, ENOMEM); @@ -2660,6 +2775,59 @@ static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, fuse_reply_err(req, 0); } +/*****************************************************************************/ +static void xfuse_cb_statfs(fuse_req_t req, fuse_ino_t ino) +{ + XFS_INODE *xinode; + + LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: ino=%ld", ino); + + if (!(xinode = xfs_get(g_xfs, ino))) + { + LOG_DEVEL(LOG_LEVEL_ERROR, "inode %ld is not valid", ino); + fuse_reply_err(req, ENOENT); + } + else if (!xinode->is_redirected) + { + /* specified file is a local resource */ + struct statvfs vfs_stats = {0}; + fuse_reply_statfs(req, &vfs_stats); + } + else + { + /* specified file resides on redirected share */ + + struct state_statfs *fip = g_new0(struct state_statfs, 1); + char *full_path = xfs_get_full_path(g_xfs, ino); + if (full_path == NULL || fip == NULL) + { + LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); + fuse_reply_err(req, ENOMEM); + free(full_path); + free(fip); + } + else + { + const char *cptr; + fip->req = req; + + /* get devredir to statfs the filesystem for the file + * + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_statfs() + */ + cptr = filename_on_device(full_path); + if (devredir_statfs(fip, xinode->device_id, cptr)) + { + LOG_DEVEL(LOG_LEVEL_ERROR, "failed to send devredir_statfs() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + free(full_path); + } + } +} + /****************************************************************************** * miscellaneous functions *****************************************************************************/ diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index a456bc9033..ae38356633 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -72,6 +72,8 @@ struct state_write; struct state_remove; struct state_rename; struct state_close; +struct statvfs; // OS structure defined in +struct state_statfs; /* functions that are invoked from devredir */ @@ -114,6 +116,10 @@ void xfuse_devredir_cb_rename_file(struct state_rename *fip, void xfuse_devredir_cb_file_close(struct state_close *fip); +void xfuse_devredir_cb_statfs(struct state_statfs *fip, + const struct statvfs *fss, + enum NTSTATUS IoStatus); + /* * Returns true if a filesystem path lies in the FUSE filesystem * diff --git a/sesman/chansrv/chansrv_xfs.c b/sesman/chansrv/chansrv_xfs.c index 1be68dafb5..d90ab71557 100644 --- a/sesman/chansrv/chansrv_xfs.c +++ b/sesman/chansrv/chansrv_xfs.c @@ -283,7 +283,9 @@ xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid) (xino1 = g_new0(XFS_INODE_ALL, 1)) == NULL || (xino2 = g_new0(XFS_INODE_ALL, 1)) == NULL || (xino1_name = strdup(".")) == NULL || - (xino2_name = strdup(".delete-pending")) == NULL) + (xino2_name = strdup(".delete-pending")) == NULL || + // This keeps Coverity happy (see below) + xfs->free_count < 3) { free(xino1); free(xino2); diff --git a/sesman/chansrv/chansrv_xfs.h b/sesman/chansrv/chansrv_xfs.h index 4b82fd202c..e28a891ab5 100644 --- a/sesman/chansrv/chansrv_xfs.h +++ b/sesman/chansrv/chansrv_xfs.h @@ -20,7 +20,13 @@ #ifndef _CHANSRV_XFS #define _CHANSRV_XFS -/* Skip this include if there's no FUSE */ +/* Maximum length of filename supported (in bytes). + * This is a sensible limit to a filename length. It is not used by + * this module to allocate long-lived storage, so it can be increased + * if necessary */ +#define XFS_MAXFILENAMELEN 1023 + +/* Skip the rest of this include if there's no FUSE */ #ifdef XRDP_FUSE #include @@ -29,12 +35,6 @@ #include "arch.h" -/* Maximum length of filename supported (in bytes). - * This is a sensible limit to a filename length. It is not used by - * this module to allocate long-lived storage, so it can be increased - * if necessary */ -#define XFS_MAXFILENAMELEN 1023 - /* * Incomplete types for the public interface */ diff --git a/sesman/chansrv/clipboard_file.c b/sesman/chansrv/clipboard_file.c index 90fed048af..0be2f6e68e 100644 --- a/sesman/chansrv/clipboard_file.c +++ b/sesman/chansrv/clipboard_file.c @@ -274,7 +274,7 @@ clipboard_get_file(const char *file, int bytes) list_add_item(g_files_list, (tintptr)cfi); cfi->size = g_file_get_size(full_fn); cfi->flags = CB_FILE_ATTRIBUTE_ARCHIVE; - cfi->time = (g_time1() + CB_EPOCH_DIFF) * 10000000LL; + cfi->time = (time(NULL) + CB_EPOCH_DIFF) * 10000000LL; LOG_DEVEL(LOG_LEVEL_DEBUG, "ok filename [%s] pathname [%s] size [%d]", cfi->filename, cfi->pathname, cfi->size); result = 0; diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index e3d58b2457..47904d0158 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -93,7 +94,9 @@ enum COMPLETION_TYPE CID_RENAME_FILE, CID_RENAME_FILE_RESP, CID_LOOKUP, - CID_SETATTR + CID_SETATTR, + CID_STATFS, + CID_STATFS_RESP }; @@ -135,6 +138,12 @@ static void devredir_proc_cid_lookup( IRP *irp, static void devredir_proc_cid_setattr( IRP *irp, struct stream *s_in, enum NTSTATUS IoStatus); +static void devredir_proc_cid_statfs( IRP *irp, + struct stream *s_in, + enum NTSTATUS IoStatus); +static void devredir_proc_cid_statfs_resp(IRP *irp, + struct stream *s_in, + enum NTSTATUS IoStatus); /* Other local functions */ static void devredir_send_server_core_cap_req(void); static void devredir_send_server_clientID_confirm(void); @@ -213,6 +222,8 @@ static const char *completion_type_to_str(enum COMPLETION_TYPE cid) (cid == CID_RENAME_FILE_RESP) ? "CID_RENAME_FILE_RESP" : (cid == CID_LOOKUP) ? "CID_LOOKUP" : (cid == CID_SETATTR) ? "CID_SETATTR" : + (cid == CID_STATFS) ? "CID_STATFS" : + (cid == CID_STATFS_RESP) ? "CID_STATFS_RESP" : /* default */ ""; }; #endif @@ -1206,6 +1217,14 @@ devredir_proc_device_iocompletion(struct stream *s) devredir_proc_cid_setattr(irp, s, IoStatus); break; + case CID_STATFS: + devredir_proc_cid_statfs(irp, s, IoStatus); + break; + + case CID_STATFS_RESP: + devredir_proc_cid_statfs_resp(irp, s, IoStatus); + break; + default: LOG_DEVEL(LOG_LEVEL_ERROR, "got unknown CompletionID: DeviceId=0x%x " "CompletionId=0x%x IoStatus=0x%x", @@ -1254,11 +1273,7 @@ devredir_proc_query_dir_response(IRP *irp, // Size the filename buffer so it's big enough for // storing the file in our filesystem if we need to. -#ifdef XFS_MAXFILENAMELEN char filename[XFS_MAXFILENAMELEN + 1]; -#else - char filename[256]; -#endif tui64 LastAccessTime; tui64 LastWriteTime; tui64 EndOfFile; @@ -1544,6 +1559,57 @@ devredir_setattr_for_entry(struct state_setattr *fusep, tui32 device_id, return rval; } +/** + * FUSE calls this function whenever it wants us to run a statfs + * + * @param fusep opaque data struct that we just pass back to FUSE when done + * @param device_id device_id of the redirected share + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int +devredir_statfs(struct state_statfs *fusep, tui32 device_id, const char *path) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval = -1; + IRP *irp; + + LOG_DEVEL(LOG_LEVEL_DEBUG, "fusep=%p", fusep); + + if ((irp = devredir_irp_with_pathname_new(path)) != NULL) + { + /* convert / to windows compatible \ */ + devredir_cvt_slash(irp->pathname); + + /* + * Allocate an IRP to open the file, read the filesystem size + * attributes and then close the file + */ + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_STATFS; + irp->DeviceId = device_id; + irp->fuse_info = fusep; + + DesiredAccess = DA_SYNCHRONIZE; + CreateOptions = 0; + CreateDisposition = CD_FILE_OPEN; + + LOG_DEVEL(LOG_LEVEL_DEBUG, "statfs for device_id=%d CompletionId=%d", + device_id, irp->CompletionId); + + rval = devredir_send_drive_create_request(device_id, + irp->pathname, + DesiredAccess, CreateOptions, + 0, CreateDisposition, + irp->CompletionId); + } + + return rval; +} + int devredir_file_create(struct state_create *fusep, tui32 device_id, const char *path, int mode) @@ -2247,7 +2313,7 @@ devredir_proc_cid_lookup(IRP *irp, LOG_DEVEL(LOG_LEVEL_ERROR, "Expected FILE_BASIC_INFORMATION length" "%d, got len=%d", FILE_BASIC_INFORMATION_SIZE, Length); - IoStatus = STATUS_UNSUCCESSFUL; + IoStatus = STATUS_INFO_LENGTH_MISMATCH; lookup_done(irp, IoStatus); } else @@ -2266,7 +2332,7 @@ devredir_proc_cid_lookup(IRP *irp, LOG_DEVEL(LOG_LEVEL_ERROR, "Expected FILE_STD_INFORMATION length" "%d, got len=%d", FILE_STD_INFORMATION_SIZE, Length); - IoStatus = STATUS_UNSUCCESSFUL; + IoStatus = STATUS_INFO_LENGTH_MISMATCH; } else { @@ -2279,6 +2345,128 @@ devredir_proc_cid_lookup(IRP *irp, } +/*****************************************************************************/ +static void +devredir_proc_cid_statfs(IRP *irp, + struct stream *s_in, + enum NTSTATUS IoStatus) +{ + struct stream *s; + int bytes; + char size_info_struct[FILE_FS_FULL_SIZE_INFORMATION_SIZE] = {0}; + + if (IoStatus != STATUS_SUCCESS) + { + struct statvfs fss = {0}; + LOG_DEVEL(LOG_LEVEL_DEBUG, + "statfs returned with IoStatus=0x%x", IoStatus); + + xfuse_devredir_cb_statfs((struct state_statfs *)irp->fuse_info, + &fss, + IoStatus); + devredir_irp_delete(irp); + return; + } + + xstream_new(s, (int)(64 + sizeof(size_info_struct))); + + irp->completion_type = CID_STATFS_RESP; + devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, + irp->CompletionId, + IRP_MJ_QUERY_VOLUME_INFORMATION, + IRP_MN_NONE); + + xstream_wr_u32_le(s, FileFsFullSizeInformation); + xstream_wr_u32_le(s, sizeof(size_info_struct)); + /* number of bytes after padding */ + xstream_seek(s, 24); /* padding */ + xstream_copyin(s, size_info_struct, sizeof(size_info_struct)); + /* Queried structure */ + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); +} + +/* [MS-RDPEFS] 2.2.3.4.6 */ +static void +devredir_proc_cid_statfs_resp(IRP *irp, + struct stream *s_in, + enum NTSTATUS IoStatus) +{ + struct statvfs fss = {0}; + tui32 Length; + if (IoStatus == STATUS_SUCCESS) + { + xstream_rd_u32_le(s_in, Length); + if (Length != FILE_FS_FULL_SIZE_INFORMATION_SIZE) + { + LOG_DEVEL(LOG_LEVEL_ERROR, + "Expected FILE_FS_FULL_SIZE_INFORMATION_SIZE length" + " %d, got len=%d", + FILE_FS_FULL_SIZE_INFORMATION_SIZE, Length); + IoStatus = STATUS_INFO_LENGTH_MISMATCH; + } + else + { + tui64 TotalAllocationUnits; + tui64 CallerAvailableAllocationUnits; + tui64 ActualAvailableAllocationUnits; + tui32 SectorsPerAllocationUnit; + tui32 BytesPerSector; + unsigned int block_size; + + xstream_rd_u64_le(s_in, TotalAllocationUnits); + xstream_rd_u64_le(s_in, CallerAvailableAllocationUnits); + xstream_rd_u64_le(s_in, ActualAvailableAllocationUnits); + xstream_rd_u32_le(s_in, SectorsPerAllocationUnit); + xstream_rd_u32_le(s_in, BytesPerSector); + + block_size = SectorsPerAllocationUnit * BytesPerSector; + if (block_size < 512 || block_size > 131072) + { + LOG(LOG_LEVEL_ERROR, + "Unreasonable block size for file system : %u", + block_size); + } + else + { + fss.f_bsize = block_size; + fss.f_frsize = block_size; + fss.f_blocks = TotalAllocationUnits; + fss.f_bfree = ActualAvailableAllocationUnits; + fss.f_bavail = CallerAvailableAllocationUnits; + // Following values do not seem to be needed by + // any applications. btrfs also returns 0 for these + //fss.f_files = ???; + //fss.f_ffree = ???; + //fss.f_favail = fss.f_ffree; + // Chromium 130 needs this set, or the user can't save + // to our filesystem + fss.f_namemax = XFS_MAXFILENAMELEN; + } + } + } + xfuse_devredir_cb_statfs((struct state_statfs *)irp->fuse_info, + &fss, IoStatus); + + if (IoStatus != STATUS_SUCCESS) + { + devredir_irp_delete(irp); + } + else + { + irp->completion_type = CID_CLOSE; + devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, IRP_MN_NONE, 32); + } +} + /* * Re-uses the specified IRP to issue a request to set basic file attributes * diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 9035de23b3..651cb2a3d2 100644 --- a/sesman/chansrv/devredir.h +++ b/sesman/chansrv/devredir.h @@ -53,6 +53,7 @@ struct state_read; struct state_write; struct state_remove; struct state_close; +struct state_statfs; /* called from FUSE module */ @@ -95,4 +96,7 @@ int devredir_rmdir_or_file(struct state_remove *fusep, tui32 device_id, const char *path); +int +devredir_statfs(struct state_statfs *fusep, tui32 device_id, const char *path); + #endif diff --git a/sesman/chansrv/rail.c b/sesman/chansrv/rail.c index 40d47dc13d..a9bac1a6ff 100644 --- a/sesman/chansrv/rail.c +++ b/sesman/chansrv/rail.c @@ -1362,7 +1362,7 @@ rail_create_window(Window window_id, Window owner_id) char *title_bytes = 0; int title_size = 0; XWindowAttributes attributes; - int style; + tui32 style; int ext_style; int num_window_rects = 1; int num_visibility_rects = 1; diff --git a/sesman/chansrv/smartcard_pcsc.c b/sesman/chansrv/smartcard_pcsc.c index 3fb78b1ff4..0584188141 100644 --- a/sesman/chansrv/smartcard_pcsc.c +++ b/sesman/chansrv/smartcard_pcsc.c @@ -732,6 +732,7 @@ scard_function_list_readers_return(void *user_data, struct trans *con; struct pcsc_list_readers *pcscListReaders; char *msz_readers = NULL; + int rv; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_list_readers_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); @@ -788,37 +789,41 @@ scard_function_list_readers_return(void *user_data, out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { - return 1; + rv = 1; } - s_push_layer(out_s, iso_hdr, 8); - out_uint32_le(out_s, llen); - out_uint32_le(out_s, readers); + else { - const char *p = msz_readers; - for (index = 0; index < readers; index++) + s_push_layer(out_s, iso_hdr, 8); + out_uint32_le(out_s, llen); + out_uint32_le(out_s, readers); { - unsigned int slen = strlen(p); - if (slen < 100) - { - out_uint8a(out_s, p, slen); - out_uint8s(out_s, 100 - slen); - } - else + const char *p = msz_readers; + for (index = 0; index < readers; index++) { - out_uint8a(out_s, p, 99); - out_uint8s(out_s, 1); + unsigned int slen = strlen(p); + if (slen < 100) + { + out_uint8a(out_s, p, slen); + out_uint8s(out_s, 100 - slen); + } + else + { + out_uint8a(out_s, p, 99); + out_uint8s(out_s, 1); + } + p += (slen + 1); } - p += (slen + 1); } + out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ + s_mark_end(out_s); + bytes = (int) (out_s->end - out_s->data); + s_pop_layer(out_s, iso_hdr); + out_uint32_le(out_s, bytes - 8); + out_uint32_le(out_s, 0x03); /* SCARD_LIST_READERS 0x03 */ + rv = trans_force_write(con); } free(msz_readers); - out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ - s_mark_end(out_s); - bytes = (int) (out_s->end - out_s->data); - s_pop_layer(out_s, iso_hdr); - out_uint32_le(out_s, bytes - 8); - out_uint32_le(out_s, 0x03); /* SCARD_LIST_READERS 0x03 */ - return trans_force_write(con); + return rv; } /*****************************************************************************/ diff --git a/sesman/chansrv/sound.c b/sesman/chansrv/sound.c index 2c862f99bd..4fd1b35a7e 100644 --- a/sesman/chansrv/sound.c +++ b/sesman/chansrv/sound.c @@ -71,7 +71,7 @@ static struct trans *g_audio_c_trans_out = 0; /* connection */ static struct trans *g_audio_l_trans_in = 0; /* listener */ static struct trans *g_audio_c_trans_in = 0; /* connection */ -static int g_training_sent_time = 0; +static unsigned int g_training_sent_time = 0; static int g_cBlockNo = 0; static int g_bytes_in_stream = 0; struct fifo *g_in_fifo; @@ -86,7 +86,7 @@ static struct stream *g_stream_incoming_packet = NULL; #define MAX_BBUF_SIZE (1024 * 16) static char g_buffer[MAX_BBUF_SIZE]; static int g_buf_index = 0; -static int g_sent_time[256]; +static unsigned int g_sent_time[256]; static int g_bbuf_size = 1024 * 8; /* may change later */ @@ -341,7 +341,7 @@ sound_send_training(void) { struct stream *s; int bytes; - int time; + unsigned int time; char *size_ptr; make_stream(s); @@ -349,7 +349,7 @@ sound_send_training(void) out_uint16_le(s, SNDC_TRAINING); size_ptr = s->p; out_uint16_le(s, 0); /* size, set later */ - time = g_time3(); + time = g_get_elapsed_ms(); g_training_sent_time = time; out_uint16_le(s, time); out_uint16_le(s, 1024); @@ -885,7 +885,7 @@ sound_send_wave_data_chunk(char *data, int data_bytes) { struct stream *s; int bytes; - int time; + unsigned int time; int format_index; char *size_ptr; @@ -912,7 +912,7 @@ sound_send_wave_data_chunk(char *data, int data_bytes) out_uint16_le(s, SNDC_WAVE); size_ptr = s->p; out_uint16_le(s, 0); /* size, set later */ - time = g_time3(); + time = g_get_elapsed_ms(); out_uint16_le(s, time); out_uint16_le(s, format_index); /* wFormatNo */ g_cBlockNo++; @@ -1040,7 +1040,7 @@ sound_process_training(struct stream *s, int size) { int time_diff; - time_diff = g_time3() - g_training_sent_time; + time_diff = g_get_elapsed_ms() - g_training_sent_time; LOG(LOG_LEVEL_INFO, "sound_process_training: round trip time %u", time_diff); return 0; } @@ -1052,12 +1052,12 @@ sound_process_wave_confirm(struct stream *s, int size) { int wTimeStamp; int cConfirmedBlockNo; - int time; + unsigned int time; int time_diff; int index; int acc; - time = g_time3(); + time = g_get_elapsed_ms(); in_uint16_le(s, wTimeStamp); in_uint8(s, cConfirmedBlockNo); time_diff = time - g_sent_time[cConfirmedBlockNo & 0xff]; @@ -1095,13 +1095,13 @@ static int process_pcm_message(int id, int size, struct stream *s) { static int sending_silence = 0; - static int silence_start_time = 0; + static unsigned int silence_start_time = 0; switch (id) { case 0: if ((g_client_does_fdk_aac || g_client_does_mp3lame) && sending_silence) { - if ((g_time3() - silence_start_time) < (int)g_cfg->msec_do_not_send) + if ((g_get_elapsed_ms() - silence_start_time) < g_cfg->msec_do_not_send) { /* do not send data within msec_do_not_send msec after SNDC_CLOSE is sent, to avoid stutter. setting from sesman.ini */ return 0; @@ -1119,7 +1119,7 @@ process_pcm_message(int id, int size, struct stream *s) if (buf != NULL) { int i; - silence_start_time = g_time3(); + silence_start_time = g_get_elapsed_ms(); sending_silence = 1; for (i = 0; i < send_silence_times; i++) { @@ -1835,6 +1835,7 @@ sound_sndsrvr_source_data_in(struct trans *trans) if (i < g_bytes_in_stream) { + //coverity[COPY_PASTE_ERROR:FALSE] xstream_copyin(s, &g_stream_inp->data[g_stream_inp->size - g_bytes_in_stream], i); bytes_read += i; g_bytes_in_stream -= i; diff --git a/sesman/chansrv/xcommon.c b/sesman/chansrv/xcommon.c index 2e444e76cc..2067bbd6f4 100644 --- a/sesman/chansrv/xcommon.c +++ b/sesman/chansrv/xcommon.c @@ -85,17 +85,6 @@ xcommon_fatal_handler(Display *dis) return 0; } -/*****************************************************************************/ -/* returns time in milliseconds - this is like g_time2 in os_calls, but not milliseconds since machine was - up, something else - this is a time value similar to what the xserver uses */ -int -xcommon_get_local_time(void) -{ - return g_time3(); -} - /******************************************************************************/ /* this should be called first */ int diff --git a/sesman/chansrv/xcommon.h b/sesman/chansrv/xcommon.h index 75e93c0957..3195eda2a9 100644 --- a/sesman/chansrv/xcommon.h +++ b/sesman/chansrv/xcommon.h @@ -28,8 +28,6 @@ typedef void (*x_server_fatal_cb_type)(void); -int -xcommon_get_local_time(void); int xcommon_init(void); int diff --git a/sesman/libsesman/Makefile.am b/sesman/libsesman/Makefile.am index 7536cfeb44..2beebd1e11 100644 --- a/sesman/libsesman/Makefile.am +++ b/sesman/libsesman/Makefile.am @@ -3,7 +3,8 @@ AM_CPPFLAGS = \ -DXRDP_PAMCONF_PATH=\"${pamconfdir}\" \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ + -DXRDP_SOCKET_ROOT_PATH=\"${socketdir}\" \ -I$(top_srcdir)/libipm \ -I$(top_srcdir)/common # -DXRDP_SBIN_PATH=\"${sbindir}\" \ diff --git a/sesman/libsesman/sesman_config.h b/sesman/libsesman/sesman_config.h index 5f403fa159..aa8d43dcfb 100644 --- a/sesman/libsesman/sesman_config.h +++ b/sesman/libsesman/sesman_config.h @@ -31,6 +31,8 @@ #include "list.h" #include "log.h" +#include "xrdp_sockets.h" + enum SESMAN_CFG_SESS_POLICY_BITS { /* If these two are set, they override everything else */ @@ -182,7 +184,7 @@ struct config_sesman * @var listen_port * @brief Listening port */ - char listen_port[256]; + char listen_port[XRDP_SOCKETS_MAXPATH]; /** * @var enable_user_wm * @brief Flag that enables user specific wm diff --git a/sesman/libsesman/verify_user.c b/sesman/libsesman/verify_user.c index ccd289d238..fad83b422a 100644 --- a/sesman/libsesman/verify_user.c +++ b/sesman/libsesman/verify_user.c @@ -225,7 +225,7 @@ auth_check_pwd_chg(const char *user) } /* check if we need a pwd change */ - now = g_time1(); + now = time(NULL); today = now / SECS_PER_DAY; if (stp->sp_expire == -1) @@ -306,7 +306,7 @@ auth_change_pwd(const char *user, const char *newpwd) } stp->sp_pwdp = g_strdup(hash); - today = g_time1() / SECS_PER_DAY; + today = time(NULL) / SECS_PER_DAY; stp->sp_lstchg = today; stp->sp_expire = today + stp->sp_max + stp->sp_inact; fd = fopen("/etc/shadow", "rw"); @@ -377,7 +377,7 @@ auth_account_disabled(struct spwd *stp) return 1; } - today = g_time1() / SECS_PER_DAY; + today = time(NULL) / SECS_PER_DAY; LOG_DEVEL(LOG_LEVEL_DEBUG, "last %ld", stp->sp_lstchg); LOG_DEVEL(LOG_LEVEL_DEBUG, "min %ld", stp->sp_min); diff --git a/sesman/scp_process.c b/sesman/scp_process.c index 8105fc6bb9..62960b370d 100644 --- a/sesman/scp_process.c +++ b/sesman/scp_process.c @@ -578,6 +578,32 @@ process_list_sessions_request(struct pre_session_item *psi) /******************************************************************************/ +static int +process_create_sockdir_request(struct pre_session_item *psi) +{ + enum scp_create_sockdir_status status = E_SCP_CS_OTHER_ERROR; + + if (psi->login_state == E_PS_LOGIN_NOT_LOGGED_IN) + { + status = E_SCP_CS_NOT_LOGGED_IN; + } + else + { + LOG(LOG_LEVEL_INFO, + "Received request from %s to create sockdir for user %s", + psi->peername, psi->username); + + if (create_xrdp_socket_path(psi->uid) == 0) + { + status = E_SCP_CS_OK; + } + } + + return scp_send_create_sockdir_response(psi->client_trans, status); +} + +/******************************************************************************/ + static int process_close_connection_request(struct pre_session_item *psi) { @@ -625,6 +651,10 @@ scp_process(struct pre_session_item *psi) rv = process_list_sessions_request(psi); break; + case E_SCP_CREATE_SOCKDIR_REQUEST: + rv = process_create_sockdir_request(psi); + break; + case E_SCP_CLOSE_CONNECTION_REQUEST: rv = process_close_connection_request(psi); break; diff --git a/sesman/sesexec/Makefile.am b/sesman/sesexec/Makefile.am index 7adb959150..1eae19cb04 100644 --- a/sesman/sesexec/Makefile.am +++ b/sesman/sesexec/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_LIBEXEC_PATH=\"${libexecdir}/xrdp\" \ -DXRDP_SOCKET_ROOT_PATH=\"${socketdir}\" \ diff --git a/sesman/sesexec/login_info.c b/sesman/sesexec/login_info.c index adb8ef1add..bb9d3f9eb7 100644 --- a/sesman/sesexec/login_info.c +++ b/sesman/sesexec/login_info.c @@ -57,8 +57,8 @@ log_authfail_message(const char *username, const char *ip_addr) { ip_addr = "unknown"; } - LOG(LOG_LEVEL_INFO, "AUTHFAIL: user=%s ip=%s time=%d", - username, ip_addr, g_time1()); + LOG(LOG_LEVEL_INFO, "AUTHFAIL: user=%s ip=%s time=%ld", + username, ip_addr, (long)time(NULL)); } /******************************************************************************/ diff --git a/sesman/sesexec/session.c b/sesman/sesexec/session.c index 11ec979d95..5a20a5f0b2 100644 --- a/sesman/sesexec/session.c +++ b/sesman/sesexec/session.c @@ -495,9 +495,6 @@ start_x_server(struct login_info *login_info, unknown_session_type = 1; } - g_free(passwd_file); - passwd_file = NULL; - if (xserver_params == NULL) { LOG(LOG_LEVEL_ERROR, "Out of memory allocating X server params"); @@ -520,6 +517,7 @@ start_x_server(struct login_info *login_info, } /* should not get here */ + g_free(passwd_file); list_delete(xserver_params); LOG(LOG_LEVEL_ERROR, "A fatal error has occurred attempting " "to start the X server on display %u, aborting connection", @@ -674,7 +672,7 @@ session_start_wrapped(struct login_info *login_info, sd->win_mgr = window_manager_pid; sd->x_server = display_pid; sd->chansrv = chansrv_pid; - sd->start_time = g_time1(); + sd->start_time = time(NULL); status = E_SCP_SCREATE_OK; } } @@ -860,7 +858,7 @@ session_process_child_exit(struct session_data *sd, } else if (pid == sd->win_mgr) { - int wm_wait_time = g_time1() - sd->start_time; + int wm_wait_time = time(NULL) - sd->start_time; if (e->reason == E_PXR_STATUS_CODE && e->val == 0) { diff --git a/sesman/sesexec_control.c b/sesman/sesexec_control.c index 422c3c2224..9a29b5e964 100644 --- a/sesman/sesexec_control.c +++ b/sesman/sesexec_control.c @@ -165,7 +165,11 @@ sesexec_start(struct pre_session_item *psi) * in the environment */ char buff[64]; g_snprintf(buff, sizeof(buff), "%d", sck[1]); - g_setenv("EICP_FD", buff, 1); + if (g_setenv("EICP_FD", buff, 1) < 0) + { + LOG(LOG_LEVEL_ERROR, "Can't set EICP_FD [%s]", + g_get_strerror()); + } /* [Development] Log all file descriptors not marked cloexec * other than stdin, stdout, stderr, and the EICP fd in sck[1]. diff --git a/sesman/sesman.c b/sesman/sesman.c index 6aafbc97d1..18cd765f0c 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -925,15 +925,15 @@ main(int argc, char **argv) LOG(LOG_LEVEL_INFO, "starting xrdp-sesman with pid %d", g_pid); - /* make sure the /tmp/.X11-unix directory exists */ - if (!g_directory_exist("/tmp/.X11-unix")) + /* make sure the X11_UNIX_SOCKET_DIRECTORY exists */ + if (!g_directory_exist(X11_UNIX_SOCKET_DIRECTORY)) { - if (!g_create_dir("/tmp/.X11-unix")) + if (!g_create_dir(X11_UNIX_SOCKET_DIRECTORY)) { LOG(LOG_LEVEL_ERROR, - "sesman.c: error creating dir /tmp/.X11-unix"); + "sesman.c: error creating dir " X11_UNIX_SOCKET_DIRECTORY); } - g_chmod_hex("/tmp/.X11-unix", 0x1777); + g_chmod_hex(X11_UNIX_SOCKET_DIRECTORY, 0x1777); } if ((error = pre_session_list_init(MAX_PRE_SESSION_ITEMS)) == 0 && diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 96089a3f1e..57a6545613 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -168,6 +168,7 @@ param=96 #FuseMountName=/run/user/%u/thinclient_drives #FuseMountName=/media/thinclient_drives/%U/thinclient_drives FuseMountName=thinclient_drives +#FuseMountNameColonCharReplacement= ; this value allows only the user to access their own mapped drives. ; Make this more permissive (e.g. 022) if required. FileUmask=077 @@ -204,6 +205,11 @@ EnableSyslog=true #EnableConsole=false #ConsoleLevel=INFO #EnableProcessId=false +; Log file path +; Set this to move the log file away from its default location. You may want +; to do this for (e.g.) NFS-mounted home directories +; See sesman.ini(5) for the format of this parameter +#LogFilePath=/run/user/%u/xrdp [ChansrvLoggingPerLogger] ; Note: per logger configuration is only used if xrdp is built with diff --git a/sesman/session_list.c b/sesman/session_list.c index ae3037ef26..b2c0ea59de 100644 --- a/sesman/session_list.c +++ b/sesman/session_list.c @@ -155,13 +155,13 @@ x_server_running_check_ports(int display) int x_running; int sck; - g_sprintf(text, "/tmp/.X11-unix/X%d", display); + g_snprintf(text, sizeof(text), X11_UNIX_SOCKET_STR, display); x_running = g_file_exist(text); if (!x_running) { LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - g_sprintf(text, "/tmp/.X%d-lock", display); + g_snprintf(text, sizeof(text), "/tmp/.X%d-lock", display); x_running = g_file_exist(text); } @@ -170,7 +170,7 @@ x_server_running_check_ports(int display) LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); if ((sck = g_tcp_socket()) != -1) { - g_sprintf(text, "59%2.2d", display); + g_snprintf(text, sizeof(text), "59%2.2d", display); x_running = g_tcp_bind(sck, text); g_tcp_close(sck); } @@ -181,7 +181,7 @@ x_server_running_check_ports(int display) LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); if ((sck = g_tcp_socket()) != -1) { - g_sprintf(text, "60%2.2d", display); + g_snprintf(text, sizeof(text), "60%2.2d", display); x_running = g_tcp_bind(sck, text); g_tcp_close(sck); } @@ -192,7 +192,7 @@ x_server_running_check_ports(int display) LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); if ((sck = g_tcp_socket()) != -1) { - g_sprintf(text, "62%2.2d", display); + g_snprintf(text, sizeof(text), "62%2.2d", display); x_running = g_tcp_bind(sck, text); g_tcp_close(sck); } diff --git a/sesman/tools/Makefile.am b/sesman/tools/Makefile.am index 73f1a3b96e..611b86eee0 100644 --- a/sesman/tools/Makefile.am +++ b/sesman/tools/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SOCKET_ROOT_PATH=\"${socketdir}\" \ -I$(top_srcdir)/sesman/libsesman \ -I$(top_srcdir)/common \ @@ -17,14 +17,10 @@ noinst_PROGRAMS = \ xrdp-xcon xrdp_sesrun_SOURCES = \ - sesrun.c \ - tools_common.h \ - tools_common.c + sesrun.c xrdp_sesadmin_SOURCES = \ - sesadmin.c \ - tools_common.h \ - tools_common.c + sesadmin.c xrdp_dis_SOURCES = \ dis.c diff --git a/sesman/tools/sesadmin.c b/sesman/tools/sesadmin.c index e41c8fed49..2265c056b8 100644 --- a/sesman/tools/sesadmin.c +++ b/sesman/tools/sesadmin.c @@ -26,11 +26,14 @@ #include "log.h" #include "os_calls.h" #include "string_calls.h" -#include "tools_common.h" + +#include "scp.h" +#include "scp_sync.h" #include #include + char cmnd[257]; char port[257]; @@ -89,25 +92,8 @@ int main(int argc, char **argv) } else { - enum scp_login_status login_result; - /* Log in as the current user */ - if ((rv = scp_send_uds_login_request(t)) == 0 && - (rv = wait_for_sesman_reply(t, E_SCP_LOGIN_RESPONSE)) == 0) - { - rv = scp_get_login_response(t, &login_result, NULL, NULL); - if (rv == 0) - { - if (login_result != E_SCP_LOGIN_OK) - { - char msg[256]; - scp_login_status_to_str(login_result, msg, sizeof(msg)); - g_printf("Login failed; %s\n", msg); - rv = 1; - } - } - scp_msg_in_reset(t); // Done with this message - } + rv = scp_sync_uds_login_request(t); } if (rv == 0) @@ -149,7 +135,9 @@ print_session(const struct scp_session_info *s) { char *username; const char *uptr; - g_getuser_info_by_uid(s->uid, &username, NULL, NULL, NULL, NULL); + // Don't need to check error return explicitly - can check username + // against NULL + (void)g_getuser_info_by_uid(s->uid, &username, NULL, NULL, NULL, NULL); uptr = (username == NULL) ? "" : username; printf("Session ID: %d\n", s->sid); @@ -169,48 +157,9 @@ print_session(const struct scp_session_info *s) static int cmndList(struct trans *t) { - struct list *sessions = list_create(); - int end_of_list = 0; - - enum scp_list_sessions_status status; - struct scp_session_info *p; - - int rv = scp_send_list_sessions_request(t); - - sessions->auto_free = 1; - - while (rv == 0 && !end_of_list) - { - rv = wait_for_sesman_reply(t, E_SCP_LIST_SESSIONS_RESPONSE); - if (rv != 0) - { - break; - } - - rv = scp_get_list_sessions_response(t, &status, &p); - if (rv != 0) - { - break; - } - - switch (status) - { - case E_SCP_LS_SESSION_INFO: - list_add_item(sessions, (tintptr)p); - break; - - case E_SCP_LS_END_OF_LIST: - end_of_list = 1; - break; - - default: - printf("Unexpected return code %d\n", status); - rv = 1; - } - scp_msg_in_reset(t); - } - - if (rv == 0) + int rv = 1; + struct list *sessions = scp_sync_list_sessions_request(t); + if (sessions != NULL) { if (sessions->count == 0) { @@ -224,9 +173,10 @@ cmndList(struct trans *t) print_session((struct scp_session_info *)sessions->items[i]); } } + (void)scp_send_close_connection_request(t); + list_delete(sessions); } - list_delete(sessions); return rv; } diff --git a/sesman/tools/sesrun.c b/sesman/tools/sesrun.c index b727da353d..89e20c36dc 100644 --- a/sesman/tools/sesrun.c +++ b/sesman/tools/sesrun.c @@ -40,7 +40,8 @@ #include "string_calls.h" #include "guid.h" -#include "tools_common.h" +#include "scp.h" +#include "scp_sync.h" // cppcheck doesn't always set this macro to something in double-quotes #if defined(__cppcheck__) @@ -453,7 +454,7 @@ handle_login_response(struct trans *t, int *server_closed) { enum scp_login_status login_result; - int rv = wait_for_sesman_reply(t, E_SCP_LOGIN_RESPONSE); + int rv = scp_sync_wait_specific(t, E_SCP_LOGIN_RESPONSE); if (rv != 0) { *server_closed = 1; @@ -511,7 +512,7 @@ handle_create_session_response(struct trans *t) int display; struct guid guid; - int rv = wait_for_sesman_reply(t, E_SCP_CREATE_SESSION_RESPONSE); + int rv = scp_sync_wait_specific(t, E_SCP_CREATE_SESSION_RESPONSE); if (rv == 0) { rv = scp_get_create_session_response(t, &status, diff --git a/sesman/tools/tools_common.c b/sesman/tools/tools_common.c deleted file mode 100644 index 674ba69e23..0000000000 --- a/sesman/tools/tools_common.c +++ /dev/null @@ -1,71 +0,0 @@ -/** - * xrdp: A Remote Desktop Protocol server. - * - * Copyright (C) Jay Sorg 2004-2022 - * - * Licensed 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. - */ - -/** - * - * @file tools_common.c - * @brief Common definitions for the tools utilities - * @author Matt Burt - * - */ - - -#if defined(HAVE_CONFIG_H) -#include "config_ac.h" -#endif - -#include "tools_common.h" -#include "trans.h" -#include "os_calls.h" -#include "scp.h" - - -/*****************************************************************************/ -int -wait_for_sesman_reply(struct trans *t, enum scp_msg_code wait_msgno) -{ - - int rv = 0; - int available = 0; - - while (rv == 0 && !available) - { - if ((rv = scp_msg_in_wait_available(t)) != 0) - { - LOG(LOG_LEVEL_ERROR, "Error waiting on sesman transport"); - } - else - { - enum scp_msg_code reply_msgno = scp_msg_in_get_msgno(t); - - available = 1; - if (reply_msgno != wait_msgno) - { - char buff[64]; - scp_msgno_to_str(reply_msgno, buff, sizeof(buff)); - - LOG(LOG_LEVEL_WARNING, - "Ignoring unexpected message %s", buff); - scp_msg_in_reset(t); - available = 0; - } - } - } - - return rv; -} diff --git a/sesman/tools/tools_common.h b/sesman/tools/tools_common.h deleted file mode 100644 index 2e357965e8..0000000000 --- a/sesman/tools/tools_common.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * xrdp: A Remote Desktop Protocol server. - * - * Copyright (C) Jay Sorg 2004-2022 - * - * Licensed 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. - */ - -/** - * - * @file tools_common.c - * @brief Common definitions for the tools utilities - * @author Matt Burt - * - */ - -#if !defined(TOOLS_COMMON_H) -#define TOOLS_COMMON_H - -#include "scp.h" - -struct trans; - -/**************************************************************************//** - * Waits for an expected reply from sesman - * - * Any other incoming messages are ignored. - * - * Following this call, the message can be parsed in the usual way. - * - * @param t SCP transport - * @param wait_msgno Code of the message we're waiting for. - * @result 0 for success - */ -int -wait_for_sesman_reply(struct trans *t, enum scp_msg_code wait_msgno); - -#endif /* TOOLS_COMMON_H */ diff --git a/tests/xrdp/Makefile.am b/tests/xrdp/Makefile.am index e9fab447f1..505e48875c 100644 --- a/tests/xrdp/Makefile.am +++ b/tests/xrdp/Makefile.am @@ -21,13 +21,18 @@ EXTRA_DIST = \ test_not4_24bit.bmp \ test1.jpg \ test_alpha_blend.png \ - gfx/gfx.toml\ - gfx/gfx_codec_order_undefined.toml \ - gfx/gfx_codec_h264_preferred.toml \ + gfx/gfx.toml \ gfx/gfx_codec_h264_only.toml \ - gfx/gfx_codec_rfx_preferred.toml \ + gfx/gfx_codec_h264_preferred.toml \ + gfx/gfx_codec_order_undefined.toml \ + gfx/gfx_codec_rfx_only.toml \ gfx/gfx_codec_rfx_preferred_odd.toml \ - gfx/gfx_codec_rfx_only.toml + gfx/gfx_codec_rfx_preferred.toml \ + gfx/gfx_h264_encoder_invalid.toml \ + gfx/gfx_h264_encoder_openh264.toml \ + gfx/gfx_h264_encoder_undefined.toml \ + gfx/gfx_h264_encoder_x264.toml \ + gfx/gfx_missing_h264.toml TESTS = test_xrdp check_PROGRAMS = test_xrdp @@ -85,3 +90,10 @@ test_xrdp_LDADD += \ $(top_builddir)/xrdp/xrdp_encoder_x264.o \ $(XRDP_X264_LIBS) endif + +if XRDP_OPENH264 +AM_CPPFLAGS += -DXRDP_X264 $(XRDP_OPENH264_CFLAGS) +test_xrdp_LDADD += \ + $(top_builddir)/xrdp/xrdp_encoder_openh264.o \ + $(XRDP_OPENH264_LIBS) +endif diff --git a/tests/xrdp/gfx/gfx.toml b/tests/xrdp/gfx/gfx.toml index af3fcf86ed..ef98c4e1f9 100644 --- a/tests/xrdp/gfx/gfx.toml +++ b/tests/xrdp/gfx/gfx.toml @@ -1,40 +1,86 @@ [codec] order = [ "H.264", "RFX" ] +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +h264_encoder = "OpenH264" + +# +# Configurations for x264 +# [x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. preset = "ultrafast" tune = "zerolatency" profile = "main" # profile is forced to baseline if preset == ultrafast vbv_max_bitrate = 0 vbv_buffer_size = 0 -fps_num = 24 +fps_num = 60 fps_den = 1 [x264.lan] # inherits default [x264.wan] -vbv_max_bitrate = 15000 -vbv_buffer_size = 1500 +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 [x264.broadband_high] preset = "superfast" -vbv_max_bitrate = 8000 +vbv_max_bitrate = 8_000 vbv_buffer_Size = 800 [x264.satellite] preset = "superfast" -vbv_max_bitrate = 5000 +vbv_max_bitrate = 5_000 vbv_buffer_size = 500 [x264.broadband_low] preset = "veryfast" -tune = "zerolatency" -vbv_max_bitrate = 1600 +vbv_max_bitrate = 1_600 vbv_buffer_size = 66 [x264.modem] preset = "fast" -tune = "zerolatency" -vbv_max_bitrate = 1200 +vbv_max_bitrate = 1_200 vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/tests/xrdp/gfx/gfx_codec_h264_only.toml b/tests/xrdp/gfx/gfx_codec_h264_only.toml index 86fa7d70cb..8a6e228bef 100644 --- a/tests/xrdp/gfx/gfx_codec_h264_only.toml +++ b/tests/xrdp/gfx/gfx_codec_h264_only.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_codec_h264_preferred.toml b/tests/xrdp/gfx/gfx_codec_h264_preferred.toml index 7d5b11ade1..4cd405d983 100644 --- a/tests/xrdp/gfx/gfx_codec_h264_preferred.toml +++ b/tests/xrdp/gfx/gfx_codec_h264_preferred.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_codec_order_undefined.toml b/tests/xrdp/gfx/gfx_codec_order_undefined.toml index 7432cb97ba..28773555fc 100644 --- a/tests/xrdp/gfx/gfx_codec_order_undefined.toml +++ b/tests/xrdp/gfx/gfx_codec_order_undefined.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_codec_rfx_only.toml b/tests/xrdp/gfx/gfx_codec_rfx_only.toml index 9ab14ea2f5..cdd974a4cf 100644 --- a/tests/xrdp/gfx/gfx_codec_rfx_only.toml +++ b/tests/xrdp/gfx/gfx_codec_rfx_only.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_codec_rfx_preferred.toml b/tests/xrdp/gfx/gfx_codec_rfx_preferred.toml index c09d029bd1..5f1f425063 100644 --- a/tests/xrdp/gfx/gfx_codec_rfx_preferred.toml +++ b/tests/xrdp/gfx/gfx_codec_rfx_preferred.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_codec_rfx_preferred_odd.toml b/tests/xrdp/gfx/gfx_codec_rfx_preferred_odd.toml index ff5c701579..66442884ac 100644 --- a/tests/xrdp/gfx/gfx_codec_rfx_preferred_odd.toml +++ b/tests/xrdp/gfx/gfx_codec_rfx_preferred_odd.toml @@ -9,6 +9,7 @@ vbv_max_bitrate = 0 vbv_buffer_size = 0 fps_num = 24 fps_den = 1 +threads = 1 [x264.lan] [x264.wan] diff --git a/tests/xrdp/gfx/gfx_h264_encoder_invalid.toml b/tests/xrdp/gfx/gfx_h264_encoder_invalid.toml new file mode 100644 index 0000000000..673dff2194 --- /dev/null +++ b/tests/xrdp/gfx/gfx_h264_encoder_invalid.toml @@ -0,0 +1,87 @@ +[codec] +order = [ "H.264", "RFX" ] + +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +h264_encoder = "FreeH264" + +# +# Configurations for x264 +# +[x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. +preset = "ultrafast" +tune = "zerolatency" +profile = "main" # profile is forced to baseline if preset == ultrafast +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 60 +fps_den = 1 +threads = 1 + +[x264.lan] +# inherits default + +[x264.wan] +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 + +[x264.broadband_high] +preset = "superfast" +vbv_max_bitrate = 8_000 +vbv_buffer_Size = 800 + +[x264.satellite] +preset = "superfast" +vbv_max_bitrate = 5_000 +vbv_buffer_size = 500 + +[x264.broadband_low] +preset = "veryfast" +vbv_max_bitrate = 1_600 +vbv_buffer_size = 66 + +[x264.modem] +preset = "fast" +vbv_max_bitrate = 1_200 +vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/tests/xrdp/gfx/gfx_h264_encoder_openh264.toml b/tests/xrdp/gfx/gfx_h264_encoder_openh264.toml new file mode 100644 index 0000000000..a49350b574 --- /dev/null +++ b/tests/xrdp/gfx/gfx_h264_encoder_openh264.toml @@ -0,0 +1,87 @@ +[codec] +order = [ "H.264", "RFX" ] + +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +h264_encoder = "OpenH264" + +# +# Configurations for x264 +# +[x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. +preset = "ultrafast" +tune = "zerolatency" +profile = "main" # profile is forced to baseline if preset == ultrafast +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 60 +fps_den = 1 +threads = 1 + +[x264.lan] +# inherits default + +[x264.wan] +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 + +[x264.broadband_high] +preset = "superfast" +vbv_max_bitrate = 8_000 +vbv_buffer_Size = 800 + +[x264.satellite] +preset = "superfast" +vbv_max_bitrate = 5_000 +vbv_buffer_size = 500 + +[x264.broadband_low] +preset = "veryfast" +vbv_max_bitrate = 1_600 +vbv_buffer_size = 66 + +[x264.modem] +preset = "fast" +vbv_max_bitrate = 1_200 +vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/tests/xrdp/gfx/gfx_h264_encoder_undefined.toml b/tests/xrdp/gfx/gfx_h264_encoder_undefined.toml new file mode 100644 index 0000000000..46dc057b5f --- /dev/null +++ b/tests/xrdp/gfx/gfx_h264_encoder_undefined.toml @@ -0,0 +1,87 @@ +[codec] +order = [ "H.264", "RFX" ] + +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +#h264_encoder = "OpenH264" + +# +# Configurations for x264 +# +[x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. +preset = "ultrafast" +tune = "zerolatency" +profile = "main" # profile is forced to baseline if preset == ultrafast +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 60 +fps_den = 1 +threads = 1 + +[x264.lan] +# inherits default + +[x264.wan] +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 + +[x264.broadband_high] +preset = "superfast" +vbv_max_bitrate = 8_000 +vbv_buffer_Size = 800 + +[x264.satellite] +preset = "superfast" +vbv_max_bitrate = 5_000 +vbv_buffer_size = 500 + +[x264.broadband_low] +preset = "veryfast" +vbv_max_bitrate = 1_600 +vbv_buffer_size = 66 + +[x264.modem] +preset = "fast" +vbv_max_bitrate = 1_200 +vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/tests/xrdp/gfx/gfx_h264_encoder_x264.toml b/tests/xrdp/gfx/gfx_h264_encoder_x264.toml new file mode 100644 index 0000000000..ee89ab95e2 --- /dev/null +++ b/tests/xrdp/gfx/gfx_h264_encoder_x264.toml @@ -0,0 +1,87 @@ +[codec] +order = [ "H.264", "RFX" ] + +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +h264_encoder = "x264" + +# +# Configurations for x264 +# +[x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. +preset = "ultrafast" +tune = "zerolatency" +profile = "main" # profile is forced to baseline if preset == ultrafast +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 60 +fps_den = 1 +threads = 1 + +[x264.lan] +# inherits default + +[x264.wan] +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 + +[x264.broadband_high] +preset = "superfast" +vbv_max_bitrate = 8_000 +vbv_buffer_Size = 800 + +[x264.satellite] +preset = "superfast" +vbv_max_bitrate = 5_000 +vbv_buffer_size = 500 + +[x264.broadband_low] +preset = "veryfast" +vbv_max_bitrate = 1_600 +vbv_buffer_size = 66 + +[x264.modem] +preset = "fast" +vbv_max_bitrate = 1_200 +vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/tests/xrdp/test_tconfig.c b/tests/xrdp/test_tconfig.c index 81184984d1..2971b2781a 100644 --- a/tests/xrdp/test_tconfig.c +++ b/tests/xrdp/test_tconfig.c @@ -14,6 +14,56 @@ START_TEST(test_tconfig_gfx_always_success) } END_TEST +START_TEST(test_tconfig_gfx_h264_oh264) +{ + struct xrdp_tconfig_gfx gfxconfig; + tconfig_load_gfx(GFXCONF_STUBDIR "/gfx_h264_encoder_openh264.toml", &gfxconfig); + + /* H.264 encoder is OpenH264 */ + ck_assert_int_eq(gfxconfig.h264_encoder, XTC_H264_OPENH264); +} + +START_TEST(test_tconfig_gfx_h264_x264) +{ + struct xrdp_tconfig_gfx gfxconfig; + tconfig_load_gfx(GFXCONF_STUBDIR "/gfx_h264_encoder_x264.toml", &gfxconfig); + + /* H.264 encoder is x264 */ + ck_assert_int_eq(gfxconfig.h264_encoder, XTC_H264_X264); +} + +START_TEST(test_tconfig_gfx_h264_undefined) +{ + struct xrdp_tconfig_gfx gfxconfig; + tconfig_load_gfx(GFXCONF_STUBDIR "/gfx_h264_encoder_undefined.toml", &gfxconfig); + + /* H.264 encoder is x264 if undefined */ + ck_assert_int_eq(gfxconfig.h264_encoder, XTC_H264_X264); +} + +START_TEST(test_tconfig_gfx_h264_invalid) +{ + struct xrdp_tconfig_gfx gfxconfig; + tconfig_load_gfx(GFXCONF_STUBDIR "/gfx_h264_encoder_invalid.toml", &gfxconfig); + + /* H.264 encoder is x264 if invalid, unknown encoder specified */ + ck_assert_int_eq(gfxconfig.h264_encoder, XTC_H264_X264); +} + +START_TEST(test_tconfig_gfx_oh264_load_basic) +{ + struct xrdp_tconfig_gfx gfxconfig; + int rv = tconfig_load_gfx(GFXCONF_STUBDIR "/gfx.toml", &gfxconfig); + + ck_assert_int_eq(rv, 0); + + /* default */ + ck_assert_int_eq(gfxconfig.openh264_param[0].EnableFrameSkip, 0); + ck_assert_int_eq(gfxconfig.openh264_param[0].TargetBitrate, 20000000); + ck_assert_int_eq(gfxconfig.openh264_param[0].MaxBitrate, 0); + ck_assert_float_eq(gfxconfig.openh264_param[0].MaxFrameRate, 60.0); +} + START_TEST(test_tconfig_gfx_x264_load_basic) { struct xrdp_tconfig_gfx gfxconfig; @@ -27,7 +77,7 @@ START_TEST(test_tconfig_gfx_x264_load_basic) ck_assert_str_eq(gfxconfig.x264_param[0].profile, "main"); ck_assert_int_eq(gfxconfig.x264_param[0].vbv_max_bitrate, 0); ck_assert_int_eq(gfxconfig.x264_param[0].vbv_buffer_size, 0); - ck_assert_int_eq(gfxconfig.x264_param[0].fps_num, 24); + ck_assert_int_eq(gfxconfig.x264_param[0].fps_num, 60); ck_assert_int_eq(gfxconfig.x264_param[0].fps_den, 1); } @@ -111,6 +161,15 @@ make_suite_tconfig_load_gfx(void) tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_missing_file); tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_missing_h264); + /* OpenH264 */ + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_oh264_load_basic); + + /* H.264 encoder */ + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_h264_oh264); + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_h264_x264); + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_h264_undefined); + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_h264_invalid); + suite_add_tcase(s, tc_tconfig_load_gfx); return s; diff --git a/tools/chkpriv/Makefile.am b/tools/chkpriv/Makefile.am index 88485f57ed..db1050fde1 100644 --- a/tools/chkpriv/Makefile.am +++ b/tools/chkpriv/Makefile.am @@ -3,7 +3,7 @@ xrdppkgdatadir=$(datadir)/xrdp pkglibexec_PROGRAMS = \ xrdp-droppriv -dist_xrdppkgdata_SCRIPTS = \ +nodist_xrdppkgdata_SCRIPTS = \ xrdp-chkpriv AM_LDFLAGS = @@ -11,7 +11,9 @@ AM_LDFLAGS = AM_CPPFLAGS = \ -I$(top_srcdir)/common -xrdp_droppriv_SOURCES = xrdp-droppriv.c +xrdp_droppriv_SOURCES = \ + xrdp-chkpriv.in \ + xrdp-droppriv.c xrdp_droppriv_LDADD = \ $(top_builddir)/common/libcommon.la \ @@ -19,7 +21,8 @@ xrdp_droppriv_LDADD = \ SUBST_VARS = sed \ -e 's|@pkglibexecdir[@]|$(pkglibexecdir)|g' \ - -e 's|@sysconfdir[@]|$(sysconfdir)|g' + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@sysconfsubdir[@]|$(sysconfsubdir)|g' subst_verbose = $(subst_verbose_@AM_V@) subst_verbose_ = $(subst_verbose_@AM_DEFAULT_V@) @@ -29,5 +32,5 @@ SUFFIXES = .in .in: $(subst_verbose)$(SUBST_VARS) $< > $@ -CLEANFILES = xrdp-chkpriv +CLEANFILES = $(nodist_xrdppkgdata_SCRIPTS) diff --git a/tools/chkpriv/xrdp-chkpriv.in b/tools/chkpriv/xrdp-chkpriv.in old mode 100755 new mode 100644 index 2f9acd8de9..32580ce444 --- a/tools/chkpriv/xrdp-chkpriv.in +++ b/tools/chkpriv/xrdp-chkpriv.in @@ -20,12 +20,41 @@ # mode # Change these if they do not match your installation -CONF_DIR=@sysconfdir@/xrdp +CONF_DIR=@sysconfdir@/@sysconfsubdir@ XRDP_INI="$CONF_DIR"/xrdp.ini SESMAN_INI="$CONF_DIR"/sesman.ini RSAKEYS_INI="$CONF_DIR"/rsakeys.ini DROPPRIV=@pkglibexecdir@/xrdp-droppriv +# Helper functions to print colored tag like "[ OK ]" + +print_ok() +{ + if [ -t 1 ]; then + printf "\033[1m[ \033[1;32mOK\033[0m ]\033[0m " + else + printf "[ OK ] " + fi +} + +print_warn() +{ + if [ -t 1 ]; then + printf "\033[1m[ \033[1;33mWARN\033[0m ]\033[0m " + else + printf "[ WARN ] " + fi +} + +print_ng() +{ + if [ -t 1 ]; then + printf "\033[1m[ \033[1;31mNG\033[0m ]\033[0m " + else + printf "[ NG ] " + fi +} + # ----------------------------------------------------------------------------- # G E T I N I V A L U E # @@ -51,6 +80,7 @@ GetIniValue() # ----------------------------------------------------------------------------- if [ "$(id -u)" != 0 ]; then + print_ng echo "** Must run this script as root" >&2 exit 1 fi @@ -92,37 +122,45 @@ echo # Basic checks on runtime user/group if [ -z "$runtime_user" ] && [ -z "$runtime_group" ]; then - echo "-Info- This system is not configured to run xrdp without privilege" + print_warn + echo "This system is not configured to run xrdp without privilege" exit 0 fi if [ -z "$runtime_user" ] || [ -z "$runtime_group" ]; then - echo "-Error- Both 'runtime_user' and 'runtime_group' must be set" + print_ng + echo "Both 'runtime_user' and 'runtime_group' must be set" errors=$(( errors + 1 )) exit 1 fi if getent passwd "$runtime_user" >/dev/null ; then - echo "-Info- runtime_user '$runtime_user' appears to exist" + print_ok + echo "runtime_user '$runtime_user' appears to exist" else - echo "-Error- runtime_user '$runtime_user' does not exist" + print_ng + echo "runtime_user '$runtime_user' does not exist" errors=$(( errors + 1 )) fi GID= if getent group "$runtime_group" >/dev/null ; then - echo "-Info- runtime_group '$runtime_group' appears to exist" + print_ok + echo "runtime_group '$runtime_group' appears to exist" GID=$(getent group xrdp | cut -d: -f3) else - echo "-Error- runtime_group '$runtime_group' does not exist" + print_ng + echo "runtime_group '$runtime_group' does not exist" errors=$(( errors + 1 )) fi # Groups agree between sesman and xrdp? if [ "$runtime_user" = "$SessionSockdirGroup" ]; then - echo "-Info- xrdp.ini and sesman.ini agree on group ownership" + print_ok + echo "xrdp.ini and sesman.ini agree on group ownership" else - echo "-Error- xrdp.ini and sesman.ini do not agree on group ownership" + print_ng + echo "xrdp.ini and sesman.ini do not agree on group ownership" errors=$(( errors + 1 )) fi @@ -144,24 +182,29 @@ if [ -e $RSAKEYS_INI ]; then set -- $(stat -c "%a %u %g" $RSAKEYS_INI) esac if [ "$1/$2/$3" = "640/0/$GID" ]; then - echo "-Info- $RSAKEYS_INI has correct permissions" + print_ok + echo "$RSAKEYS_INI has correct permissions" else if [ "$1" != 640 ]; then - echo "-Error- $RSAKEYS_INI should have permissions -rw-r-----" + print_ng + echo "$RSAKEYS_INI should have permissions -rw-r-----" errors=$(( errors + 1 )) fi if [ "$2" != 0 ]; then - echo "-Error- $RSAKEYS_INI should be owned by root" + print_ng + echo "$RSAKEYS_INI should be owned by root" errors=$(( errors + 1 )) fi if [ "$3" != "$GID" ]; then - echo "-Error- $RSAKEYS_INI should be in the $runtime_group group" + print_ng + echo "$RSAKEYS_INI should be in the $runtime_group group" errors=$(( errors + 1 )) fi fi fi else - echo "-Error- $RSAKEYS_INI does not exist" + print_ng + echo "$RSAKEYS_INI does not exist" errors=$(( errors + 1 )) fi @@ -172,26 +215,32 @@ fi # group to obtain access to /etc/ssl/private/ssl-cert-snakeoil.key for file in "$certificate" "$key_file"; do if ! [ -e $file ]; then - echo "-Error- $file does not exist" + print_ng + echo "$file does not exist" errors=$(( errors + 1 )) elif ! $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -r '"$file"' ]' then - echo "-Error- $file is not readable by $runtime_user:$runtime_group" + print_ng + echo "$file is not readable by $runtime_user:$runtime_group" errors=$(( errors + 1 )) elif $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -w '"$file"' ]' then - echo "-Error- $file is writeable by $runtime_user:$runtime_group" + print_ng + echo "$file is writeable by $runtime_user:$runtime_group" errors=$(( errors + 1 )) else - echo "-Info- $file is read-only for $runtime_user:$runtime_group" + print_ok + echo "$file is read-only for $runtime_user:$runtime_group" fi done echo if [ $errors -eq 0 ]; then + print_ok echo "-Summary- Permissions appear to be correct to run xrdp unprivileged" status=0 else + print_ng echo "-Summary- $errors error(s) found. Please correct these and try again" status=1 fi diff --git a/tools/chkpriv/xrdp-droppriv.c b/tools/chkpriv/xrdp-droppriv.c old mode 100755 new mode 100644 index 185f575b10..13f6dd3dfd --- a/tools/chkpriv/xrdp-droppriv.c +++ b/tools/chkpriv/xrdp-droppriv.c @@ -23,7 +23,7 @@ #include "config_ac.h" #endif -#include "os_calls.c" +#include "os_calls.h" #include "log.h" int main(int argc, char *argv[]) diff --git a/tools/devel/tcp_proxy/main.c b/tools/devel/tcp_proxy/main.c index 137a64ee47..85505ebd61 100644 --- a/tools/devel/tcp_proxy/main.c +++ b/tools/devel/tcp_proxy/main.c @@ -43,8 +43,6 @@ int g_loc_io_count = 0; // bytes read from local port int g_rem_io_count = 0; // bytes read from remote port static int g_terminated = 0; -static char g_buf[1024 * 32]; - typedef unsigned short tui16; @@ -67,29 +65,92 @@ g_tcp_socket_ok(int sck) return 0; } +/*****************************************************************************/ +static int +copy_sck_to_sck(int from_sck, int to_sck, int hexdump, int local) +{ + char buff[1024 * 32]; + int rv = -1; + + int count = g_tcp_recv(from_sck, buff, sizeof(buff), 0); + if (count > 0 && count <= (int)sizeof(buff)) + { + rv = count; // Assume we'll return the amount of data copied + if (local) + { + g_loc_io_count += count; + if (hexdump) + { + LOG_HEXDUMP(LOG_LEVEL_INFO, "from local:", buff, count); + } + } + else + { + g_rem_io_count += count; + if (hexdump) + { + LOG_HEXDUMP(LOG_LEVEL_INFO, "from remote:", buff, count); + } + } + + + LOG(LOG_LEVEL_DEBUG, "local_io_count: %d\tremote_io_count: %d", + g_loc_io_count, g_rem_io_count); + + const char *p = buff; + while ((count > 0) && (!g_terminated)) + { + int error = g_tcp_send(to_sck, p, count, 0); + + if (error > 0 && error <= count) + { + // We wrote some data + count -= error; + p += error; + } + else if ((error == -1) && g_tcp_last_error_would_block(to_sck)) + { + if (g_tcp_can_send(to_sck, 1000)) + { + g_tcp_socket_ok(to_sck); + } + } + else + { + count = 0; // Terminate loop + rv = -1; // tell user + } + } + } + + return rv; +} + /*****************************************************************************/ static int main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) { - int lis_sck; - int acc_sck; - int con_sck; + int lis_sck = -1; + int acc_sck = -1; + int con_sck = -1; int sel; int count; - int sent; int error; int i; - int acc_to_con; - int con_to_acc; - - acc_to_con = 0; - con_to_acc = 0; - acc_sck = 0; + int acc_to_con = 0; + int con_to_acc = 0; /* create the listening socket and setup options */ lis_sck = g_tcp_socket(); - g_tcp_set_non_blocking(lis_sck); - error = g_tcp_bind(lis_sck, local_port); + if (lis_sck < 0) + { + error = 1; + } + else + { + g_tcp_set_non_blocking(lis_sck); + error = g_tcp_bind(lis_sck, local_port); + } if (error != 0) { @@ -135,7 +196,7 @@ main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) /* stop listening */ g_tcp_close(lis_sck); - lis_sck = 0; + lis_sck = -1; if (error == 0) { @@ -144,22 +205,26 @@ main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) } /* connect outgoing socket */ - con_sck = 0; - if (error == 0) { con_sck = g_tcp_socket(); - g_tcp_set_non_blocking(con_sck); - error = g_tcp_connect(con_sck, remote_ip, remote_port); + if (con_sck < 0) + { + error = 1; + } + else + { + g_tcp_set_non_blocking(con_sck); + error = g_tcp_connect(con_sck, remote_ip, remote_port); + } if ((error == -1) && g_tcp_last_error_would_block(con_sck)) { error = 0; i = 0; - while (!(g_tcp_can_send(con_sck, 100) && g_tcp_socket_ok(con_sck)) - && (!g_terminated) - && (i < 100)) + while (!g_terminated && i < 100 && + !g_tcp_can_send(con_sck, 100)) { g_sleep(100); i++; @@ -170,8 +235,7 @@ main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) LOG(LOG_LEVEL_ERROR, "timeout connecting"); error = 1; } - - if (g_terminated) + else if (!g_tcp_socket_ok(con_sck)) { error = 1; } @@ -183,7 +247,7 @@ main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) } } - while ((!g_terminated) && (error == 0)) + while (!g_terminated) { sel = g_tcp_select(con_sck, acc_sck); @@ -196,93 +260,37 @@ main_loop(char *local_port, char *remote_ip, char *remote_port, int hexdump) if (sel & 1) { // can read from con_sck w/o blocking - count = g_tcp_recv(con_sck, g_buf, 1024 * 16, 0); - error = count < 1; - - if (error == 0) + count = copy_sck_to_sck(con_sck, acc_sck, hexdump, 1); + if (count < 0) { - g_loc_io_count += count; - con_to_acc += count; - - if (hexdump) - { - LOG_HEXDUMP(LOG_LEVEL_INFO, "from remove, the socket from connect", g_buf, count); - } - - LOG(LOG_LEVEL_DEBUG, "local_io_count: %d\tremote_io_count: %d", - g_loc_io_count, g_rem_io_count); - sent = 0; - - while ((sent < count) && (error == 0) && (!g_terminated)) - { - i = g_tcp_send(acc_sck, g_buf + sent, count - sent, 0); - - if ((i == -1) && g_tcp_last_error_would_block(acc_sck)) - { - if (g_tcp_can_send(acc_sck, 1000)) - { - g_tcp_socket_ok(acc_sck); - } - } - else if (i < 1) - { - error = 1; - } - else - { - sent += i; - } - } + break; } + con_to_acc += count; } - if (sel & 2) { // can read from acc_sck w/o blocking - count = g_tcp_recv(acc_sck, g_buf, 1024 * 16, 0); - error = count < 1; - - if (error == 0) + count = copy_sck_to_sck(acc_sck, con_sck, hexdump, 0); + if (count < 0) { - g_rem_io_count += count; - acc_to_con += count; - - if (hexdump) - { - LOG_HEXDUMP(LOG_LEVEL_INFO, "from accepted, the socket from accept", g_buf, count); - } - - LOG(LOG_LEVEL_DEBUG, "local_io_count: %d\tremote_io_count: %d", - g_loc_io_count, g_rem_io_count); - sent = 0; - - while ((sent < count) && (error == 0) && (!g_terminated)) - { - i = g_tcp_send(con_sck, g_buf + sent, count - sent, 0); - - if ((i == -1) && g_tcp_last_error_would_block(con_sck)) - { - if (g_tcp_can_send(con_sck, 1000)) - { - g_tcp_socket_ok(con_sck); - } - } - else if (i < 1) - { - error = 1; - } - else - { - sent += i; - } - } - } + break; + }; + acc_to_con += count; } } - g_tcp_close(lis_sck); - g_tcp_close(con_sck); - g_tcp_close(acc_sck); + if (lis_sck >= 0) + { + g_tcp_close(lis_sck); + } + if (con_sck >= 0) + { + g_tcp_close(con_sck); + } + if (acc_sck >= 0) + { + g_tcp_close(acc_sck); + } LOG(LOG_LEVEL_INFO, "acc_to_con %d", acc_to_con); LOG(LOG_LEVEL_INFO, "con_to_acc %d", con_to_acc); return 0; diff --git a/vnc/Makefile.am b/vnc/Makefile.am index ee5ab23a81..8a4339c93e 100644 --- a/vnc/Makefile.am +++ b/vnc/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/vnc/vnc.c b/vnc/vnc.c index d433ee2822..5d19874477 100644 --- a/vnc/vnc.c +++ b/vnc/vnc.c @@ -2141,7 +2141,7 @@ lib_mod_set_param(struct vnc *v, const char *name, const char *value) } else if (g_strcasecmp(name, "disabled_encodings_mask") == 0) { - v->enabled_encodings_mask = ~g_atoi(value); + v->enabled_encodings_mask = (unsigned int)~g_atoi(value); } else if (g_strcasecmp(name, "client_info") == 0) { diff --git a/vnc/vnc_clip.c b/vnc/vnc_clip.c index 8054a24298..f7803574b0 100644 --- a/vnc/vnc_clip.c +++ b/vnc/vnc_clip.c @@ -397,7 +397,7 @@ static int handle_cb_format_data_request(struct vnc *v, struct stream *s) { int format = 0; - struct stream *out_s; + struct stream *out_s = NULL; int i; struct vnc_clipboard_data *vc = v->vc; int rv = 0; @@ -416,7 +416,6 @@ handle_cb_format_data_request(struct vnc *v, struct stream *s) LOG_DEVEL(LOG_LEVEL_INFO, "RDP client requested data format=%s", cf2text(format, scratch, sizeof(scratch))); - make_stream(out_s); /* For all formats, we need to convert to Windows carriage control, * so we need to know how many '\n' characters become '\r\n' */ @@ -448,8 +447,11 @@ handle_cb_format_data_request(struct vnc *v, struct stream *s) /* Allocate the stream and check for failure as the string could be * essentially unlimited in length */ - init_stream(out_s, alloclen); - if (out_s->data == NULL) + if ((make_stream(out_s)) != NULL) + { + init_stream(out_s, alloclen); + } + if (out_s == NULL || out_s->data == NULL) { LOG(LOG_LEVEL_ERROR, "Memory exhausted allocating %d bytes for clip data response", @@ -512,8 +514,8 @@ handle_cb_format_data_request(struct vnc *v, struct stream *s) s_mark_end(out_s); send_stream_to_clip_channel(v, out_s); - free_stream(out_s); } + free_stream(out_s); return rv; } diff --git a/waitforx/Makefile.am b/waitforx/Makefile.am index d4ddaa45ab..6b3836cb3f 100644 --- a/waitforx/Makefile.am +++ b/waitforx/Makefile.am @@ -4,6 +4,7 @@ pkglibexec_PROGRAMS = \ AM_LDFLAGS = $(X_LIBS) -lX11 -lXrandr AM_CPPFLAGS = \ + -DXRDP_SOCKET_ROOT_PATH=\"${socketdir}\" \ -I$(top_srcdir)/sesman/sesexec \ -I$(top_srcdir)/common diff --git a/waitforx/waitforx.c b/waitforx/waitforx.c index b9dabedfe2..c12c2f1430 100644 --- a/waitforx/waitforx.c +++ b/waitforx/waitforx.c @@ -2,12 +2,15 @@ #include #include #include +#include #include #include +#include #include "config_ac.h" #include "os_calls.h" #include "string_calls.h" +#include "xrdp_sockets.h" #include "xwait.h" // For return status codes #define ATTEMPTS 10 @@ -21,24 +24,124 @@ alarm_handler(int signal_num) * * Prefix the message with a newline in case another message * has been partly output */ - const char msg[] = "\nTimed out waiting for RandR outputs\n"; + const char msg[] = "\nTimed out waiting for X display\n"; g_file_write(1, msg, g_strlen(msg)); exit(XW_STATUS_TIMED_OUT); } +/*****************************************************************************/ +/*** + * Checks whether display can be reached via a Unix Domain Socket socket. + * + * Local displays can be reached by a Unix Domain socket. The display + * string will be of the form ':n' or ':n.m' where 'n' and 'm' + * are unsigned numbers + * + * @param display Display string + * @param[out] sock_name, or "" + * @param sock_name_len Length of sock_name + * @return !=0 if sock_name is not NULL + */ +static int +get_display_sock_name(const char *display, char *sock_name, + size_t sock_name_len) +{ + int local = 0; + int dnum = 0; + if (display != NULL && *display++ == ':' && isdigit(*display)) + { + do + { + if (dnum > (INT_MAX / 10 - 1)) + { + break; // Avoid signed integer overflow + } + dnum = (dnum * 10) + (*display - '0'); + ++display; + } + while (isdigit(*display)); + + // Skip the optional screen identifier + if (*display == '.' && isdigit(*(display + 1))) + { + do + { + ++display; + } + while (isdigit(*display)); + } + + local = (*display == '\0'); + } + + if (local) + { + snprintf(sock_name, sock_name_len, X11_UNIX_SOCKET_STR, dnum); + } + else + { + sock_name[0] = '\0'; + } + + + return (sock_name[0] != '\0'); +} + /*****************************************************************************/ static Display * open_display(const char *display) { + char sock_name[XRDP_SOCKETS_MAXPATH]; + int local_fd = -1; Display *dpy = NULL; unsigned int wait = ATTEMPTS; unsigned int n; - for (n = 1; n <= ATTEMPTS; ++n) + // If the display is local, we try to connect to the X11 socket for + // the display first. If we can't do this, we don't attempt to open + // the display. + // + // This is to ensure the display open code in libxcb doesn't attempt + // to connect to the X server over TCP. This can block if the network + // is configured in an unexpected way, which leads to use failing + // to detect the X server starting up shortly after. + // + // Some versions of libxcb support a 'unix:' prefix to the display + // string to allow a connection to be restricted to a local socket. + // This is not documented, and varies significantly between versions + // of libxcb. We can't use it here. + if (get_display_sock_name(display, sock_name, sizeof(sock_name)) != 0) + { + for (n = 1; n <= wait ; ++n) + { + printf("Opening socket %s. Attempt %u of %u\n", + sock_name, n, wait); + if ((local_fd = g_sck_local_socket()) >= 0) + { + if (g_sck_local_connect(local_fd, sock_name) == 0) + { + printf("Socket '%s' open succeeded.\n", sock_name); + break; + } + else + { + printf("Socket '%s' open failed [%s].\n", + sock_name, g_get_strerror()); + g_file_close(local_fd); + local_fd = -1; + } + } + g_sleep(1000); + } + + // Subtract the wait time for this stage from the overall wait time + wait -= (n - 1); + } + + for (n = 1; n <= wait; ++n) { - printf("Opening display %s. Attempt %u of %u\n", display, n, wait); - dpy = XOpenDisplay(display); - if (dpy != NULL) + printf("Opening display '%s'. Attempt %u of %u\n", display, n, wait); + if ((dpy = XOpenDisplay(display)) != NULL) { printf("Opened display %s\n", display); break; @@ -46,6 +149,13 @@ open_display(const char *display) g_sleep(1000); } + // Close the file after we try the display open, to prevent + // a display reset if our connect was the last client. + if (local_fd >= 0) + { + g_file_close(local_fd); + } + return dpy; } diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am index 4603cf654a..4d4e53f670 100644 --- a/xrdp/Makefile.am +++ b/xrdp/Makefile.am @@ -4,7 +4,7 @@ EXTRA_DIST = \ xrdpwin.c AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ @@ -33,6 +33,13 @@ XRDP_EXTRA_LIBS += $(XRDP_X264_LIBS) XRDP_EXTRA_SOURCES += xrdp_encoder_x264.c xrdp_encoder_x264.h endif +if XRDP_OPENH264 +AM_CPPFLAGS += -DXRDP_OPENH264 +AM_CPPFLAGS += $(XRDP_OPENH264_CFLAGS) +XRDP_EXTRA_LIBS += $(XRDP_OPENH264_LIBS) +XRDP_EXTRA_SOURCES += xrdp_encoder_openh264.c xrdp_encoder_openh264.h +endif + if XRDP_PIXMAN AM_CPPFLAGS += -DXRDP_PIXMAN AM_CPPFLAGS += $(PIXMAN_CFLAGS) @@ -85,7 +92,7 @@ xrdp_LDADD = \ $(IMLIB2_LIBS) \ $(XRDP_EXTRA_LIBS) -xrdpsysconfdir=$(sysconfdir)/xrdp +xrdpsysconfdir=$(sysconfdir)/$(sysconfsubdir) if MACOS lib_extension = dylib diff --git a/xrdp/gfx.toml b/xrdp/gfx.toml index af3fcf86ed..ae4a897411 100644 --- a/xrdp/gfx.toml +++ b/xrdp/gfx.toml @@ -1,40 +1,87 @@ [codec] order = [ "H.264", "RFX" ] +# Specify a preferred H.264 encoder, "x264" or "OpenH264". +# This parameter takes effect only when more than one encoder is +# enabled at compile time. If only one encoder is enabled, the encoder +# will be used regardless the value of this parameter. +h264_encoder = "x264" + +# +# Configurations for x264 +# [x264.default] +# NOTE: x264 specifies bitrate in unit of kbps. preset = "ultrafast" tune = "zerolatency" profile = "main" # profile is forced to baseline if preset == ultrafast vbv_max_bitrate = 0 vbv_buffer_size = 0 -fps_num = 24 +fps_num = 60 fps_den = 1 +threads = 1 # recommended: 1 or 2, see `man gfx.toml` for details [x264.lan] # inherits default [x264.wan] -vbv_max_bitrate = 15000 -vbv_buffer_size = 1500 +vbv_max_bitrate = 15_000 +vbv_buffer_size = 1_500 [x264.broadband_high] preset = "superfast" -vbv_max_bitrate = 8000 +vbv_max_bitrate = 8_000 vbv_buffer_Size = 800 [x264.satellite] preset = "superfast" -vbv_max_bitrate = 5000 +vbv_max_bitrate = 5_000 vbv_buffer_size = 500 [x264.broadband_low] preset = "veryfast" -tune = "zerolatency" -vbv_max_bitrate = 1600 +vbv_max_bitrate = 1_600 vbv_buffer_size = 66 [x264.modem] preset = "fast" -tune = "zerolatency" -vbv_max_bitrate = 1200 +vbv_max_bitrate = 1_200 vbv_buffer_size = 50 + +# +# Configurations for OpenH264 +# +[OpenH264.default] +# NOTE: OpenH264 specifies bitrate in unit of bps, not kbps. +EnableFrameSkip = false +TargetBitrate = 20_000_000 +MaxBitrate = 0 # unspecified +MaxFrameRate = 60.0 + +[OpenH264.lan] +# inherits default + +[OpenH264.wan] +TargetBitrate = 10_000_000 +MaxBitrate = 12_000_000 + +[OpenH264.broadband_high] +EnableFrameSkip = true +TargetBitrate = 8_000_000 +MaxBitrate = 10_000_000 + +[OpenH264.satellite] +EnableFrameSkip = true +TargetBitrate = 4_000_000 +MaxBitrate = 6_000_000 + +[OpenH264.broadband_low] +EnableFrameSkip = true +TargetBitrate = 1_600_000 +MaxBitrate = 1_800_000 + +[OpenH264.modem] +EnableFrameSkip = true +TargetBitrate = 600_000 +MaxBitrate = 1_200_000 + diff --git a/xrdp/lang.c b/xrdp/lang.c index 77280e7389..23db4a122f 100644 --- a/xrdp/lang.c +++ b/xrdp/lang.c @@ -37,6 +37,21 @@ ((d) >= 'a' && (d) <= 'f') ? (d) - 'a' + 10 : \ (d) - 'A' + 10) +/* + * Struct representing the contents of a km file [General] section + */ +struct km_general +{ + unsigned int version; + int caps_lock_supported; +}; + +const struct km_general km_general_default = +{ + .version = 0, + .caps_lock_supported = 1 +}; + /*****************************************************************************/ struct xrdp_key_info * get_key_info_from_kbd_event(int keyboard_flags, int key_code, int *keys, @@ -53,6 +68,9 @@ get_key_info_from_kbd_event(int keyboard_flags, int key_code, int *keys, rv = 0; index = scancode_to_index(SCANCODE_FROM_KBD_EVENT(key_code, keyboard_flags)); + // Don't take caps_lock into account if the keymap doesn't support it. + caps_lock = caps_lock && keymap->caps_lock_supported; + if (index >= 0) { // scancode_to_index() guarantees to map numlock scancodes @@ -100,46 +118,6 @@ get_key_info_from_kbd_event(int keyboard_flags, int key_code, int *keys, return rv; } -/*****************************************************************************/ -int -get_keysym_from_kbd_event(int keyboard_flags, int key_code, int *keys, - int caps_lock, int num_lock, int scroll_lock, - struct xrdp_keymap *keymap) -{ - struct xrdp_key_info *ki; - - ki = get_key_info_from_kbd_event(keyboard_flags, key_code, keys, - caps_lock, num_lock, scroll_lock, - keymap); - - if (ki == 0) - { - return 0; - } - - return ki->sym; -} - -/*****************************************************************************/ -char32_t -get_char_from_kbd_event(int keyboard_flags, int key_code, int *keys, - int caps_lock, int num_lock, int scroll_lock, - struct xrdp_keymap *keymap) -{ - struct xrdp_key_info *ki; - - ki = get_key_info_from_kbd_event(keyboard_flags, key_code, keys, - caps_lock, num_lock, scroll_lock, - keymap); - - if (ki == 0) - { - return 0; - } - - return ki->chr; -} - /*****************************************************************************/ /** * Converts a table key to a scancode index value @@ -353,6 +331,101 @@ get_keymaps(int keylayout, struct xrdp_keymap *keymap) return 0; } +/*****************************************************************************/ +/** + * Parses the [General] section in a keymap file + * @param tfile TOML file in memory + * @param general result (initialised to defaults) + */ +static void +parse_km_general(toml_table_t *tfile, struct km_general *general) +{ + toml_table_t *section; + + if (tfile != NULL && (section = toml_table_in(tfile, "General")) != NULL) + { + toml_datum_t d; + if ((d = toml_int_in(section, "version")).ok) + { + general->version = d.u.i; + } + if ((d = toml_bool_in(section, "caps_lock_supported")).ok) + { + general->caps_lock_supported = d.u.b; + } + } +} + +/*****************************************************************************/ +/** + * Loads the [General] section only from a TOML file + * @param filename Name of TOML file + * @param quiet Set true to not log errors + * @param[out] km_general Contents of [General] section. Defaults are provided. + * @return 0 if the operation was successful + */ +static int +km_load_file_general(const char *filename, int quiet, + struct km_general *general) +{ + FILE *fp; + toml_table_t *tfile; + char errbuf[200]; + int rv = 1; + + *general = km_general_default; + + if ((fp = fopen(filename, "r")) == NULL) + { + if (!quiet) + { + LOG(LOG_LEVEL_ERROR, "Error loading keymap file %s (%s)", + filename, g_get_strerror()); + } + } + else + { + tfile = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (tfile == NULL) + { + if (!quiet) + { + LOG(LOG_LEVEL_ERROR, "Error in keymap file %s - %s", + filename, errbuf); + } + } + else + { + parse_km_general(tfile, general); + rv = 0; + toml_free(tfile); + } + } + + return rv; +} + +/*****************************************************************************/ +/** + * Boolean to test if a keylayout supports the caps_lock modifier key + * @param keylayout keyboardLayout from TS_UD_CS_CORE (see [MS-RDPBCGR]) + * @return True if layout supports caps lock + */ +static int +keylayout_supports_caps_lock(int keylayout) +{ + char filename[256]; + struct km_general general; + + g_snprintf(filename, sizeof(filename), + XRDP_CFG_PATH "/km-%08x.toml", keylayout); + + (void)km_load_file_general(filename, 1, &general); + + return general.caps_lock_supported; +} + /*****************************************************************************/ int km_load_file(const char *filename, struct xrdp_keymap *keymap) @@ -361,6 +434,7 @@ km_load_file(const char *filename, struct xrdp_keymap *keymap) toml_table_t *tfile; char errbuf[200]; int rv = 0; + struct km_general general = km_general_default; if ((fp = fopen(filename, "r")) == NULL) { @@ -382,16 +456,23 @@ km_load_file(const char *filename, struct xrdp_keymap *keymap) /* Clear the whole keymap */ memset(keymap, 0, sizeof(*keymap)); + /* Check to see if we should expect caps lock entries */ + parse_km_general(tfile, &general); + keymap->caps_lock_supported = general.caps_lock_supported; + /* read the keymap sections */ km_read_section(tfile, "noshift", keymap->keys_noshift); km_read_section(tfile, "shift", keymap->keys_shift); km_read_section(tfile, "altgr", keymap->keys_altgr); km_read_section(tfile, "shiftaltgr", keymap->keys_shiftaltgr); - km_read_section(tfile, "capslock", keymap->keys_capslock); - km_read_section(tfile, "capslockaltgr", keymap->keys_capslockaltgr); - km_read_section(tfile, "shiftcapslock", keymap->keys_shiftcapslock); - km_read_section(tfile, "shiftcapslockaltgr", - keymap->keys_shiftcapslockaltgr); + if (keymap->caps_lock_supported) + { + km_read_section(tfile, "capslock", keymap->keys_capslock); + km_read_section(tfile, "capslockaltgr", keymap->keys_capslockaltgr); + km_read_section(tfile, "shiftcapslock", keymap->keys_shiftcapslock); + km_read_section(tfile, "shiftcapslockaltgr", + keymap->keys_shiftcapslockaltgr); + } /* The numlock map is much smaller and offset by * SCANCODE_MIX_NUMLOCK. Read the section into a temporary @@ -657,8 +738,16 @@ xrdp_init_xkb_layout(struct xrdp_client_info *client_info) // Initialise the rules and a few keycodes for xorgxrdp snprintf(client_info->xkb_rules, sizeof(client_info->xkb_rules), "%s", scancode_get_xkb_rules()); - client_info->x11_keycode_caps_lock = - scancode_to_x11_keycode(SCANCODE_CAPS_KEY); + if (keylayout_supports_caps_lock(client_info->keylayout)) + { + client_info->x11_keycode_caps_lock = + scancode_to_x11_keycode(SCANCODE_CAPS_KEY); + } + else + { + LOG(LOG_LEVEL_INFO, "xrdp_init_xkb_layout: caps lock is not supported"); + client_info->x11_keycode_caps_lock = 0; + } client_info->x11_keycode_num_lock = scancode_to_x11_keycode(SCANCODE_NUMLOCK_KEY); client_info->x11_keycode_scroll_lock = diff --git a/xrdp/xrdp.h b/xrdp/xrdp.h index c40557ebb3..96ee93c222 100644 --- a/xrdp/xrdp.h +++ b/xrdp/xrdp.h @@ -442,14 +442,6 @@ get_key_info_from_kbd_event(int keyboard_flags, int key_code, int *keys, int caps_lock, int num_lock, int scroll_lock, struct xrdp_keymap *keymap); int -get_keysym_from_kbd_event(int keyboard_flags, int key_code, int *keys, - int caps_lock, int num_lock, int scroll_lock, - struct xrdp_keymap *keymap); -char32_t -get_char_from_kbd_event(int keyboard_flags, int key_code, int *keys, - int caps_lock, int num_lock, int scroll_lock, - struct xrdp_keymap *keymap); -int get_keymaps(int keylayout, struct xrdp_keymap *keymap); int @@ -497,8 +489,8 @@ struct display_control_monitor_layout_data { struct display_size_description description; enum display_resize_state state; - int last_state_update_timestamp; - int start_time; + unsigned int last_state_update_timestamp; + unsigned int start_time; /// This flag is set if the state machine needs to /// shutdown/startup EGFX int using_egfx; diff --git a/xrdp/xrdp.ini.in b/xrdp/xrdp.ini.in index 53924764cc..5bd79c78e8 100644 --- a/xrdp/xrdp.ini.in +++ b/xrdp/xrdp.ini.in @@ -249,6 +249,10 @@ password=ask port=-1 code=20 #keycode_set=evdev +; Frame capture interval (milliseconds) +h264_frame_interval=16 +rfx_frame_interval=32 +normal_frame_interval=40 [Xvnc] name=Xvnc @@ -262,10 +266,6 @@ port=-1 ; Disable requested encodings to support buggy VNC servers ; (1 = ExtendedDesktopSize) #disabled_encodings_mask=0 -; Use this to connect to a chansrv instance created outside of sesman -; (e.g. as part of an x11vnc console session). Replace '0' with the -; display number of the session -#chansrvport=DISPLAY(0) ; Generic VNC Proxy ; Tailor this to specific hosts and VNC instances by specifying an ip @@ -280,6 +280,15 @@ password=ask #pamusername=asksame #pampassword=asksame #delay_ms=2000 +; Use one of these to connect to a chansrv instance created outside of sesman +; (e.g. as part of an x11vnc console session). Replace 'n' with the +; display number of the session, and (if applicable) 'u' with the numeric +; UID of the session. +; +; If 'username' or 'pamusername' is set, you probably don't need to use +; the two parameter variant with 'u'. +#chansrvport=DISPLAY(n) +#chansrvport=DISPLAY(n,u) ; Generic RDP proxy using NeutrinoRDP ; Tailor this to specific hosts by specifying an ip and port and setting diff --git a/xrdp/xrdp_bitmap.c b/xrdp/xrdp_bitmap.c index 34ef7cab34..005d935daa 100644 --- a/xrdp/xrdp_bitmap.c +++ b/xrdp/xrdp_bitmap.c @@ -29,7 +29,10 @@ #include "log.h" #include "string_calls.h" - +// For a very few key functions, using the keysym is preferable to the +// raw scancode. Here are defines to avoid pulling an X11 dependency +// into the xrdp:- +#define XK_BackSpace 0xff08 static const unsigned int g_crc_table[256] = @@ -1150,6 +1153,13 @@ xrdp_bitmap_def_proc(struct xrdp_bitmap *self, int msg, { int scan_code = SCANCODE_FROM_KBD_EVENT(param1, param2); int num_lock = self->wm->num_lock; + /* We may need a keysym or a printable character for the key */ + struct xrdp_key_info *ki = get_key_info_from_kbd_event + (param2, param1, self->wm->keys, + self->wm->caps_lock, + self->wm->num_lock, self->wm->scroll_lock, + &(self->wm->keymap)); + /* left or up arrow */ if ((scan_code == SCANCODE_LEFT_ARROW_KEY) || (scan_code == SCANCODE_UP_ARROW_KEY) || @@ -1174,8 +1184,9 @@ xrdp_bitmap_def_proc(struct xrdp_bitmap *self, int msg, xrdp_bitmap_invalidate(self, 0); } } - /* backspace */ - else if (scan_code == SCANCODE_BACKSPACE_KEY) + /* backspace. Test keysym rather than scan code, so keys + * other than SCANCODE_BACKSPACE_KEY can generate backspace */ + else if (ki != NULL && ki->sym == XK_BackSpace) { n = utf8_char_count(self->caption1); @@ -1229,10 +1240,7 @@ xrdp_bitmap_def_proc(struct xrdp_bitmap *self, int msg, } else { - char32_t c = get_char_from_kbd_event - (param2, param1, self->wm->keys, self->wm->caps_lock, - self->wm->num_lock, self->wm->scroll_lock, - &(self->wm->keymap)); + char32_t c = (ki == NULL) ? 0 : ki->chr; // Add a printing character to the string. If successful, // bump the edit position and re-display the string if (c >= ' ' && diff --git a/xrdp/xrdp_egfx.c b/xrdp/xrdp_egfx.c index 7c5b76de57..095e9e5a29 100644 --- a/xrdp/xrdp_egfx.c +++ b/xrdp/xrdp_egfx.c @@ -800,6 +800,7 @@ xrdp_egfx_process_capsadvertise(struct xrdp_egfx *egfx, struct stream *s) char *holdp; int *versions; int *flagss; + int rv = 0; LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process_capsadvertise:"); if (egfx->caps_advertise == NULL) @@ -813,46 +814,46 @@ xrdp_egfx_process_capsadvertise(struct xrdp_egfx *egfx, struct stream *s) } caps_count = 0; versions = g_new(int, capsSetCount); - if (versions == NULL) - { - return 1; - } flagss = g_new(int, capsSetCount); - if (flagss == NULL) + if (versions == NULL || flagss == NULL) { - g_free(versions); - return 1; + rv = 1; } - for (index = 0; index < capsSetCount; index++) + else { - if (!s_check_rem(s, 8)) + for (index = 0; index < capsSetCount; index++) { - return 1; - } - in_uint32_le(s, version); - in_uint32_le(s, capsDataLength); - if (!s_check_rem(s, capsDataLength)) - { - return 1; - } - holdp = s->p; - // This implicity excludes caps version 101. - if (capsDataLength == 4) - { - in_uint32_le(s, flags); - versions[caps_count] = version; - flagss[caps_count] = flags; - caps_count++; + if (!s_check_rem(s, 8)) + { + rv = 1; + break; + } + in_uint32_le(s, version); + in_uint32_le(s, capsDataLength); + if (!s_check_rem(s, capsDataLength)) + { + rv = 1; + break; + } + holdp = s->p; + // This implicity excludes caps version 101. + if (capsDataLength == 4) + { + in_uint32_le(s, flags); + versions[caps_count] = version; + flagss[caps_count] = flags; + caps_count++; + } + s->p = holdp + capsDataLength; } - s->p = holdp + capsDataLength; } - if (caps_count > 0) + if (rv == 0 && caps_count > 0) { egfx->caps_advertise(egfx->user, caps_count, versions, flagss); } g_free(versions); g_free(flagss); - return 0; + return rv; } /******************************************************************************/ diff --git a/xrdp/xrdp_encoder.c b/xrdp/xrdp_encoder.c index ed9b4abb09..aa8870690f 100644 --- a/xrdp/xrdp_encoder.c +++ b/xrdp/xrdp_encoder.c @@ -38,6 +38,10 @@ #include "xrdp_encoder_x264.h" #endif +#ifdef XRDP_OPENH264 +#include "xrdp_encoder_openh264.h" +#endif + #define DEFAULT_XRDP_GFX_FRAMES_IN_FLIGHT 2 /* limits used for validate env var XRDP_GFX_FRAMES_IN_FLIGHT */ #define MIN_XRDP_GFX_FRAMES_IN_FLIGHT 1 @@ -94,7 +98,7 @@ process_enc_jpg(struct xrdp_encoder *self, XRDP_ENC_DATA *enc); static int process_enc_rfx(struct xrdp_encoder *self, XRDP_ENC_DATA *enc); #endif -#ifdef XRDP_X264 +#if defined(XRDP_X264) || defined(XRDP_OPENH264) static int process_enc_h264(struct xrdp_encoder *self, XRDP_ENC_DATA *enc); #endif @@ -128,6 +132,56 @@ xrdp_enc_data_done_destructor(void *item, void *closure) g_free(enc_done); } +/*****************************************************************************/ +/** + * Sets the methods used by the software H.264 module + */ +static void +set_h264_encoder_methods(struct xrdp_encoder *self) +{ + const char *encoder_name = NULL; +#if defined(XRDP_X264) && defined(XRDP_OPENH264) + struct xrdp_tconfig_gfx gfxconfig; + tconfig_load_gfx(GFX_CONF, &gfxconfig); + + switch (gfxconfig.h264_encoder) + { + case XTC_H264_OPENH264: + encoder_name = "OpenH264"; + self->xrdp_encoder_h264_create = xrdp_encoder_openh264_create; + self->xrdp_encoder_h264_delete = xrdp_encoder_openh264_delete; + self->xrdp_encoder_h264_encode = xrdp_encoder_openh264_encode; + break; + case XTC_H264_X264: + default: + /* x264 is the default H.264 software encoder */ + encoder_name = "x264"; + self->xrdp_encoder_h264_create = xrdp_encoder_x264_create; + self->xrdp_encoder_h264_delete = xrdp_encoder_x264_delete; + self->xrdp_encoder_h264_encode = xrdp_encoder_x264_encode; + break; + } +#elif defined(XRDP_OPENH264) + encoder_name = "OpenH264"; + self->xrdp_encoder_h264_create = xrdp_encoder_openh264_create; + self->xrdp_encoder_h264_delete = xrdp_encoder_openh264_delete; + self->xrdp_encoder_h264_encode = xrdp_encoder_openh264_encode; +#elif defined(XRDP_X264) + encoder_name = "x264"; + self->xrdp_encoder_h264_create = xrdp_encoder_x264_create; + self->xrdp_encoder_h264_delete = xrdp_encoder_x264_delete; + self->xrdp_encoder_h264_encode = xrdp_encoder_x264_encode; +#endif + + // Don't log the library we're going to use if we + // couldn't load it. + if (encoder_name != NULL && self->mm->libh264_loaded) + { + LOG(LOG_LEVEL_INFO, "xrdp_encoder_create: using %s for " + "software encoder", encoder_name); + } +} + /*****************************************************************************/ struct xrdp_encoder * xrdp_encoder_create(struct xrdp_mm *mm) @@ -171,8 +225,8 @@ xrdp_encoder_create(struct xrdp_mm *mm) client_info->capture_format = XRDP_a8b8g8r8; self->process_enc = process_enc_jpg; } -#ifdef XRDP_X264 - else if (mm->egfx_flags & XRDP_EGFX_H264) +#if defined(XRDP_X264) || defined(XRDP_OPENH264) + else if (mm->libh264_loaded && (mm->egfx_flags & XRDP_EGFX_H264) != 0) { LOG(LOG_LEVEL_INFO, "xrdp_encoder_create: starting h264 codec session gfx"); @@ -181,7 +235,7 @@ xrdp_encoder_create(struct xrdp_mm *mm) client_info->capture_format = XRDP_nv12_709fr; self->gfx = 1; } - else if (client_info->h264_codec_id != 0) + else if (mm->libh264_loaded && client_info->h264_codec_id != 0) { LOG(LOG_LEVEL_INFO, "xrdp_encoder_create: starting h264 codec session"); self->codec_id = client_info->h264_codec_id; @@ -309,6 +363,8 @@ xrdp_encoder_create(struct xrdp_mm *mm) /* make sure frames_in_flight is at least 1 */ self->frames_in_flight = MAX(self->frames_in_flight, 1); + set_h264_encoder_methods(self); + /* create thread to process messages */ tc_thread_create(proc_enc_msg, self); @@ -354,17 +410,17 @@ xrdp_encoder_delete(struct xrdp_encoder *self) } #endif -#if defined(XRDP_X264) +#if defined(XRDP_X264) || defined(XRDP_OPENH264) for (index = 0; index < 16; index++) { if (self->codec_handle_h264_gfx[index] != NULL) { - xrdp_encoder_x264_delete(self->codec_handle_h264_gfx[index]); + self->xrdp_encoder_h264_delete(self->codec_handle_h264_gfx[index]); } } if (self->codec_handle_h264 != NULL) { - xrdp_encoder_x264_delete(self->codec_handle_h264); + self->xrdp_encoder_h264_delete(self->codec_handle_h264); } #endif @@ -628,7 +684,7 @@ process_enc_rfx(struct xrdp_encoder *self, XRDP_ENC_DATA *enc) } #endif -#if defined(XRDP_X264) +#if defined(XRDP_X264) || defined(XRDP_OPENH264) /*****************************************************************************/ static int @@ -691,7 +747,6 @@ process_enc_h264(struct xrdp_encoder *self, XRDP_ENC_DATA *enc) LOG_DEVEL(LOG_LEVEL_INFO, "process_enc_h264: dummy func"); return 0; } - #endif /*****************************************************************************/ @@ -734,7 +789,7 @@ gfx_wiretosurface1(struct xrdp_encoder *self, struct xrdp_egfx_bulk *bulk, struct stream *in_s, XRDP_ENC_DATA *enc) { -#ifdef XRDP_X264 +#if defined(XRDP_X264) || defined(XRDP_OPENH264) int index; int surface_id; int codec_id; @@ -901,7 +956,7 @@ gfx_wiretosurface1(struct xrdp_encoder *self, if (self->codec_handle_h264_gfx[mon_index] == NULL) { self->codec_handle_h264_gfx[mon_index] = - xrdp_encoder_x264_create(); + self->xrdp_encoder_h264_create(); if (self->codec_handle_h264_gfx[mon_index] == NULL) { g_free(s->data); @@ -909,7 +964,7 @@ gfx_wiretosurface1(struct xrdp_encoder *self, return NULL; } } - error = xrdp_encoder_x264_encode( + error = self->xrdp_encoder_h264_encode( self->codec_handle_h264_gfx[mon_index], 0, 0, 0, width, height, twidth, theight, 0, diff --git a/xrdp/xrdp_encoder.h b/xrdp/xrdp_encoder.h index 3996fac540..fdf4dab55c 100644 --- a/xrdp/xrdp_encoder.h +++ b/xrdp/xrdp_encoder.h @@ -14,6 +14,16 @@ struct xrdp_enc_data; +typedef void *(*xrdp_encoder_h264_create_proc)(void); +typedef int (*xrdp_encoder_h264_delete_proc)(void *handle); +typedef int (*xrdp_encoder_h264_encode_proc)( + void *handle, int session, int left, int top, + int width, int height, int twidth, int theight, + int format, const char *data, + short *crects, int num_crects, + char *cdata, int *cdata_bytes, + int connection_type, int *flags_ptr); + /* for codec mode operations */ struct xrdp_encoder { @@ -46,6 +56,10 @@ struct xrdp_encoder int quant_idx_y; int quant_idx_u; int quant_idx_v; + int pad0; + xrdp_encoder_h264_create_proc xrdp_encoder_h264_create; + xrdp_encoder_h264_delete_proc xrdp_encoder_h264_delete; + xrdp_encoder_h264_encode_proc xrdp_encoder_h264_encode; }; /* cmd_id = 0 */ diff --git a/xrdp/xrdp_encoder_openh264.c b/xrdp/xrdp_encoder_openh264.c new file mode 100644 index 0000000000..f3fda5ed84 --- /dev/null +++ b/xrdp/xrdp_encoder_openh264.c @@ -0,0 +1,356 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2016-2024 + * Copyright (C) Christopher Pitstick 2023-2024 + * + * Licensed 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. + * + * openh264 Encoder + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "xrdp.h" +#include "arch.h" +#include "os_calls.h" +#include "xrdp_encoder_openh264.h" +#include "xrdp_tconfig.h" + +#define OPENH264_MAX_ENCODERS 16 + +struct openh264_encoder +{ + ISVCEncoder *openh264_enc_han; + char *yuvdata; + int width; + int height; +}; + +struct openh264_global +{ + struct openh264_encoder encoders[OPENH264_MAX_ENCODERS]; + struct xrdp_tconfig_gfx_openh264_param + openh264_param[NUM_CONNECTION_TYPES]; +}; + +/* The method invocations on ISVCEncoder are different for C and C++, as + ISVCEncoder is a true class in C++, but an emulated one in C */ +#ifdef __cplusplus /* compiling with g++ */ +#define ENC_GET_DEFAULT_PARAMS(obj, pParam) (obj)->GetDefaultParams(pParam) +#define ENC_INITIALIZE_EXT(obj, pParam) (obj)->InitializeExt(pParam) +#define ENC_ENCODE_FRAME(obj, kpSrcPic, pBsInfo) \ + (obj)->EncodeFrame(kpSrcPic, pBsInfo) +#else +#define ENC_GET_DEFAULT_PARAMS(obj, pParam) (*obj)->GetDefaultParams(obj, pParam) +#define ENC_INITIALIZE_EXT(obj, pParam) (*obj)->InitializeExt(obj, pParam) +#define ENC_ENCODE_FRAME(obj, kpSrcPic, pBsInfo) \ + (*obj)->EncodeFrame(obj, kpSrcPic, pBsInfo) +#endif + +/*****************************************************************************/ +void * +xrdp_encoder_openh264_create(void) +{ + struct openh264_global *og; + struct xrdp_tconfig_gfx gfxconfig; + + LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_encoder_openh264_create:"); + og = g_new0(struct openh264_global, 1); + tconfig_load_gfx(GFX_CONF, &gfxconfig); + memcpy(&og->openh264_param, &gfxconfig.openh264_param, + sizeof(struct xrdp_tconfig_gfx_openh264_param) * + NUM_CONNECTION_TYPES); + return og; + +} + +/*****************************************************************************/ +int +xrdp_encoder_openh264_delete(void *handle) +{ + struct openh264_global *og; + struct openh264_encoder *oe; + int index; + + if (handle == NULL) + { + return 0; + } + og = (struct openh264_global *) handle; + for (index = 0; index < 16; index++) + { + oe = &(og->encoders[index]); + if (oe->openh264_enc_han != NULL) + { + WelsDestroySVCEncoder(oe->openh264_enc_han); + } + g_free(oe->yuvdata); + } + g_free(og); + return 0; +} + +/*****************************************************************************/ +int +xrdp_encoder_openh264_encode(void *handle, int session, int left, int top, + int width, int height, int twidth, int theight, + int format, const char *data, + short *crects, int num_crects, + char *cdata, int *cdata_bytes, + int connection_type, int *flags_ptr) +{ + struct openh264_global *og; + struct openh264_encoder *oe; + const char *src8; + const char *src8a; + char *dst8; + char *dst8a; + char *dst8b; + char *dst8c; + int index; + int jndex; + int flags; + int x; + int y; + int cx; + int cy; + int ct; /* connection_type */ + SSourcePicture pic1; + SFrameBSInfo info; + SLayerBSInfo *layer; + SEncParamExt encParamExt; + SSpatialLayerConfig *slc; + int status; + int layer_position; + char *write_location; + unsigned char *payload; + int size; + int lcdata_bytes; + + LOG(LOG_LEVEL_TRACE, "xrdp_encoder_openh264_encode:"); + flags = 0; + og = (struct openh264_global *) handle; + oe = &(og->encoders[session % OPENH264_MAX_ENCODERS]); + /* validate connection type */ + ct = connection_type; + if (ct > CONNECTION_TYPE_LAN || ct < CONNECTION_TYPE_MODEM) + { + ct = CONNECTION_TYPE_LAN; + } + if ((oe->openh264_enc_han == NULL) || + (oe->width != width) || (oe->height != height)) + { + if (oe->openh264_enc_han != NULL) + { + LOG(LOG_LEVEL_INFO, "xrdp_encoder_openh264_encode: " + "WelsDestroySVCEncoder %p", oe->openh264_enc_han); + WelsDestroySVCEncoder(oe->openh264_enc_han); + oe->openh264_enc_han = NULL; + g_free(oe->yuvdata); + oe->yuvdata = NULL; + flags |= 2; + } + if ((width > 0) && (height > 0)) + { + status = WelsCreateSVCEncoder(&oe->openh264_enc_han); + if ((status != 0) || (oe->openh264_enc_han == NULL)) + { + LOG(LOG_LEVEL_ERROR, "Failed to create H.264 encoder"); + return 1; + } + LOG(LOG_LEVEL_INFO, "xrdp_encoder_openh264_encode: " + "WelsCreateSVCEncoder rv %p for width %d height %d", + oe->openh264_enc_han, width, height); + status = ENC_GET_DEFAULT_PARAMS( + oe->openh264_enc_han, &encParamExt); + LOG(LOG_LEVEL_INFO, "xrdp_encoder_openh264_encode: " + "GetDefaultParams rv %d", status); + if (status == 0) + { + encParamExt.iUsageType = CAMERA_VIDEO_REAL_TIME; + encParamExt.iPicWidth = (width + 15) & ~15; + encParamExt.iPicHeight = (height + 15) & ~15; + encParamExt.iRCMode = RC_BITRATE_MODE; + encParamExt.iSpatialLayerNum = 1; + /* Set encode parameters from config */ + encParamExt.bEnableFrameSkip = og->openh264_param[ct].EnableFrameSkip; + encParamExt.iTargetBitrate = og->openh264_param[ct].TargetBitrate; + encParamExt.iMaxBitrate = og->openh264_param[ct].MaxBitrate; + encParamExt.fMaxFrameRate = og->openh264_param[ct].MaxFrameRate; + /* defaults to INCREASING_ID, Mac client needs CONSTANT_ID */ + encParamExt.eSpsPpsIdStrategy = CONSTANT_ID; + slc = encParamExt.sSpatialLayers + 0; + slc->fFrameRate = encParamExt.fMaxFrameRate; + slc->iVideoWidth = encParamExt.iPicWidth; + slc->iVideoHeight = encParamExt.iPicHeight; + slc->iSpatialBitrate = encParamExt.iTargetBitrate; + slc->iMaxSpatialBitrate = encParamExt.iMaxBitrate; + status = ENC_INITIALIZE_EXT( + oe->openh264_enc_han, &encParamExt); + LOG(LOG_LEVEL_INFO, "xrdp_encoder_openh264_encode: " + "InitializeExt rv %d", status); + } + oe->yuvdata = g_new(char, (width + 16) * (height + 16) * 2); + if (oe->yuvdata == NULL) + { + WelsDestroySVCEncoder(oe->openh264_enc_han); + oe->openh264_enc_han = NULL; + return 2; + } + flags |= 1; + } + oe->width = width; + oe->height = height; + } + + if ((data != NULL) && (oe->openh264_enc_han != NULL)) + { + g_memset(&pic1, 0, sizeof(pic1)); + pic1.iPicWidth = (width + 15) & ~15; + pic1.iPicHeight = (height + 15) & ~15; + pic1.iColorFormat = videoFormatI420; + pic1.iStride[0] = pic1.iPicWidth; + pic1.iStride[1] = pic1.iPicWidth / 2; + pic1.iStride[2] = pic1.iPicWidth / 2; + pic1.pData[0] = (unsigned char *) (oe->yuvdata); + pic1.pData[1] = pic1.pData[0] + pic1.iPicWidth * pic1.iPicHeight; + pic1.pData[2] = pic1.pData[1] + (pic1.iPicWidth / 2) * + (pic1.iPicHeight / 2); + for (index = 0; index < num_crects; index++) + { + src8 = data; + dst8 = (char *) (pic1.pData[0]); + x = crects[index * 4 + 0]; + y = crects[index * 4 + 1]; + cx = crects[index * 4 + 2]; + cy = crects[index * 4 + 3]; + LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_encoder_openh264_encode: " + "x %d y %d cx %d cy %d", x, y, cx, cy); + src8 += twidth * y + x; + dst8 += pic1.iStride[0] * (y - top) + (x - left); + for (; cy > 0; cy -= 1) + { + g_memcpy(dst8, src8, cx); + src8 += twidth; + dst8 += pic1.iStride[0]; + } + } + for (index = 0; index < num_crects; index++) + { + src8 = data; /* uv */ + src8 += twidth * theight; + dst8 = (char *) (pic1.pData[1]); /* u */ + dst8a = (char *) (pic1.pData[2]); /* v */ + x = crects[index * 4 + 0]; + y = crects[index * 4 + 1]; + cx = crects[index * 4 + 2]; + cy = crects[index * 4 + 3]; + src8 += twidth * (y / 2) + x; + dst8 += pic1.iStride[1] * ((y - top) / 2) + ((x - left) / 2); + dst8a += pic1.iStride[2] * ((y - top) / 2) + ((x - left) / 2); + for (; cy > 0; cy -= 2) + { + src8a = src8; /* uv */ + dst8b = dst8; /* u */ + dst8c = dst8a; /* v */ + for (jndex = 0; jndex < cx; jndex += 2) + { + *(dst8b++) = *(src8a++); /* u */ + *(dst8c++) = *(src8a++); /* v */ + } + src8 += twidth; /* uv */ + dst8 += pic1.iStride[1]; /* u */ + dst8a += pic1.iStride[2]; /* v */ + } + } + g_memset(&info, 0, sizeof(info)); + status = ENC_ENCODE_FRAME(oe->openh264_enc_han, &pic1, &info); + if (status != 0) + { + LOG(LOG_LEVEL_TRACE, "OpenH264: Failed to encode frame"); + return 3; + } + if (info.eFrameType == videoFrameTypeSkip) + { + LOG(LOG_LEVEL_TRACE, "OpenH264: frame was skipped!"); + return 4; + } + lcdata_bytes = 0; + for (index = 0; index < info.iLayerNum; index++) + { + layer_position = 0; + layer = info.sLayerInfo + index; + for (jndex = 0; jndex < layer->iNalCount; jndex++) + { + write_location = cdata + lcdata_bytes; + payload = layer->pBsBuf + layer_position; + size = layer->pNalLengthInByte[jndex]; + if (lcdata_bytes + size > *cdata_bytes) + { + LOG(LOG_LEVEL_INFO, "out of room"); + return 5; + } + g_memcpy(write_location, payload, size); + layer_position += size; + lcdata_bytes += size; + } + } + *cdata_bytes = lcdata_bytes; + } + if (flags_ptr != NULL) + { + *flags_ptr = flags; + } + return 0; +} + +/*****************************************************************************/ +int +xrdp_encoder_openh264_install_ok(void) +{ + int rv; + + // Declare something with maximal alignment we can take the address + // of to pass to WelsCreateSVCEncoder. This object is not directly + // accessed. + // + // Note we can't use the ISVCEncoder type directly, as in C++ this + // is an abstract class. + long double dummy; + + ISVCEncoder *p = (ISVCEncoder *)&dummy; + + // The real OpenH264 library will ALWAYS change the value of the + // passed-in pointer + // The noopenh264 library will NEVER change the value of the passed-in + // pointer + // For both libraries, the relevant source is in + // codec/encoder/plus/src/welsEncoderExt.cpp + WelsCreateSVCEncoder(&p); + rv = (p != (ISVCEncoder *)&dummy); // Did the passed-in value change + // If p is &dummy or NULL, this call does nothing, otherwise resources + // are deallocated. + WelsDestroySVCEncoder(p); + + return rv; +} diff --git a/xrdp/xrdp_encoder_openh264.h b/xrdp/xrdp_encoder_openh264.h new file mode 100644 index 0000000000..42a6b2607b --- /dev/null +++ b/xrdp/xrdp_encoder_openh264.h @@ -0,0 +1,50 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2016-2024 + * Copyright (C) Christopher Pitstick 2023-2024 + * + * Licensed 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. + * + * openh264 Encoder + */ + +#ifndef _XRDP_ENCODER_OPENH264_H +#define _XRDP_ENCODER_OPENH264_H + +#include "arch.h" + +void * +xrdp_encoder_openh264_create(void); +int +xrdp_encoder_openh264_delete(void *handle); +int +xrdp_encoder_openh264_encode(void *handle, int session, int left, int top, + int width, int height, int twidth, int theight, + int format, const char *data, + short *crects, int num_crects, + char *cdata, int *cdata_bytes, + int connection_type, int *flags_ptr); + +/** + * Test OpenH264 library is installed correctly + * + * It's possible on some distros to install the noopenh264 package + * (https://gitlab.com/freedesktop-sdk/noopenh264) for building. If this + * is installed at runtime, openh264 cannot be used. + * @return Boolean (!= 0 -> working) + */ +int +xrdp_encoder_openh264_install_ok(void); + +#endif diff --git a/xrdp/xrdp_encoder_x264.c b/xrdp/xrdp_encoder_x264.c index 29f1dcc932..01622ccaa9 100644 --- a/xrdp/xrdp_encoder_x264.c +++ b/xrdp/xrdp_encoder_x264.c @@ -153,7 +153,7 @@ xrdp_encoder_x264_encode(void *handle, int session, int left, int top, x264_param_default_preset(&(xe->x264_params), xg->x264_param[ct].preset, xg->x264_param[ct].tune); - xe->x264_params.i_threads = 1; + xe->x264_params.i_threads = xg->x264_param[ct].threads; xe->x264_params.i_width = (width + 15) & ~15; xe->x264_params.i_height = (height + 15) & ~15; xe->x264_params.i_fps_num = xg->x264_param[ct].fps_num; diff --git a/xrdp/xrdp_keyboard.ini b/xrdp/xrdp_keyboard.ini index b7aa34969d..4c8208aa86 100644 --- a/xrdp/xrdp_keyboard.ini +++ b/xrdp/xrdp_keyboard.ini @@ -58,13 +58,16 @@ layouts_map=default_layouts_map [default_rdp_layouts] rdp_layout_us=0x00000409 +rdp_layout_us_colemak=0x00060409 rdp_layout_us_dvorak=0x00010409 rdp_layout_us_dvp=0x19360409 +rdp_layout_cz=0x00000405 rdp_layout_dk=0x00000406 rdp_layout_de=0x00000407 rdp_layout_es=0x0000040A rdp_layout_fi=0x0000040B rdp_layout_fr=0x0000040C +rdp_layout_hu=0x0000040E rdp_layout_it=0x00000410 rdp_layout_jp=0x00000411 rdp_layout_jp=0xe0010411 @@ -86,13 +89,16 @@ rdp_layout_pt=0x00000816 ; = [default_layouts_map] rdp_layout_us=us +rdp_layout_us_colemak=us(colemak) rdp_layout_us_dvorak=us(dvorak) rdp_layout_us_dvp=us(dvp) +rdp_layout_cz=cz rdp_layout_dk=dk rdp_layout_de=de rdp_layout_es=es rdp_layout_fi=fi rdp_layout_fr=fr +rdp_layout_hu=hu rdp_layout_it=it rdp_layout_jp=jp rdp_layout_kr=kr @@ -125,13 +131,16 @@ layouts_map=default_layouts_map [rdp_layouts_map_mac] rdp_layout_us=us +rdp_layout_us_colemak=us(colemak) rdp_layout_us_dvorak=us(dvorak) rdp_layout_us_dvp=us(dvp) +rdp_layout_cz=cz rdp_layout_dk=dk rdp_layout_de=de rdp_layout_es=es rdp_layout_fi=fi rdp_layout_fr=fr +rdp_layout_hu=hu rdp_layout_it=it rdp_layout_jp=jp rdp_layout_kr=kr diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index c3ae935dc8..68be2c3b04 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -37,6 +37,10 @@ #include "xrdp_channel.h" #include +#if defined(XRDP_OPENH264) +#include "xrdp_encoder_openh264.h" +#endif + /* Forward declarations */ static int xrdp_mm_chansrv_connect(struct xrdp_mm *self, const char *port); @@ -46,6 +50,26 @@ xrdp_mm_connect_sm(struct xrdp_mm *self); static int xrdp_mm_send_unicode_shutdown(struct xrdp_mm *self, struct trans *trans); +/*****************************************************************************/ +static void +init_libh264_loaded(struct xrdp_mm *self) +{ +#if defined(XRDP_OPENH264) + // Note that if this fails, and x264 is also configured, x264 + // will not be considered as a fallback. + self->libh264_loaded = xrdp_encoder_openh264_install_ok(); + if (!self->libh264_loaded) + { + LOG(LOG_LEVEL_ERROR, "OpenH264 Codec is not installed correctly. " + "H.264 will not be used"); + } +#elif defined (XRDP_H264) + self->libh264_loaded = 1; +#else + self->libh264_loaded = 0; +#endif +} + /*****************************************************************************/ struct xrdp_mm * xrdp_mm_create(struct xrdp_wm *owner) @@ -61,6 +85,8 @@ xrdp_mm_create(struct xrdp_wm *owner) self->uid = -1; /* Never good to default UIDs to 0 */ + init_libh264_loaded(self); + LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_mm_create: bpp %d mcs_connection_type %d " "jpeg_codec_id %d v3_codec_id %d rfx_codec_id %d " "h264_codec_id %d", @@ -72,7 +98,7 @@ xrdp_mm_create(struct xrdp_wm *owner) self->wm->client_info->h264_codec_id); if ((self->wm->client_info->gfx == 0) && - ((self->wm->client_info->h264_codec_id != 0) || + ((self->wm->client_info->h264_codec_id != 0 && self->libh264_loaded) || (self->wm->client_info->jpeg_codec_id != 0) || (self->wm->client_info->rfx_codec_id != 0))) { @@ -1275,14 +1301,14 @@ advance_resize_state_machine(struct xrdp_mm *mm, "advance_resize_state_machine:" " Processing resize to: %d x %d." " Advancing state from %s to %s." - " Previous state took %d MS.", + " Previous state took %u MS.", description->description.session_width, description->description.session_height, XRDP_DISPLAY_RESIZE_STATE_TO_STR(description->state), XRDP_DISPLAY_RESIZE_STATE_TO_STR(new_state), - g_time3() - description->last_state_update_timestamp); + g_get_elapsed_ms() - description->last_state_update_timestamp); description->state = new_state; - description->last_state_update_timestamp = g_time3(); + description->last_state_update_timestamp = g_get_elapsed_ms(); g_set_wait_obj(mm->resize_ready); return 0; } @@ -1829,7 +1855,7 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm) // ever is, advance the state machine! if (chan->drdynvcs[mm->egfx->channel_id].status == XRDP_DRDYNVC_STATUS_CLOSED - || (g_time3() - description->last_state_update_timestamp) > 100) + || (g_get_elapsed_ms() - description->last_state_update_timestamp) > 100) { advance_resize_state_machine(mm, WMRZ_EGFX_CONN_CLOSED); break; @@ -2045,7 +2071,7 @@ dynamic_monitor_process_queue(struct xrdp_mm *self) g_malloc(LAYOUT_DATA_SIZE, 1); g_memcpy(&(self->resize_data->description), queue_head, sizeof(struct display_size_description)); - const int time = g_time3(); + const unsigned int time = g_get_elapsed_ms(); self->resize_data->start_time = time; self->resize_data->last_state_update_timestamp = time; self->resize_data->using_egfx = (self->egfx != NULL); @@ -2072,10 +2098,10 @@ dynamic_monitor_process_queue(struct xrdp_mm *self) if (self->resize_data->state == WMRZ_COMPLETE) { LOG(LOG_LEVEL_INFO, "dynamic_monitor_process_queue: Clearing" - " completed resize (w: %d x h: %d). It took %d milliseconds.", + " completed resize (w: %d x h: %d). It took %u milliseconds.", self->resize_data->description.session_width, self->resize_data->description.session_height, - g_time3() - self->resize_data->start_time); + g_get_elapsed_ms() - self->resize_data->start_time); g_set_wait_obj(self->resize_ready); } else if (self->resize_data->state == WMRZ_ERROR) @@ -3010,28 +3036,60 @@ static int parse_chansrvport(const char *value, char *dest, int dest_size, int uid) { int rv = 0; + int dnum = 0; if (g_strncmp(value, "DISPLAY(", 8) == 0) { const char *p = value + 8; const char *end = p; - /* Check next chars are digits followed by ')' */ + /* Check next chars are digits */ while (isdigit(*end)) { ++end; } - if (end == p || *end != ')') + if (end == p) { - LOG(LOG_LEVEL_WARNING, "Ignoring invalid chansrvport string '%s'", + LOG(LOG_LEVEL_WARNING, + "Ignoring chansrvport string with bad display number '%s'", value); - rv = -1; + return -1; } - else + + dnum = g_atoi(p); + + if (*end == ',') { - g_snprintf(dest, dest_size, XRDP_CHANSRV_STR, uid, g_atoi(p)); + /* User has specified a UID override + * Check next chars are digits */ + p = end + 1; + end = p; + + while (isdigit(*end)) + { + ++end; + } + + if (end == p) + { + LOG(LOG_LEVEL_WARNING, + "Ignoring chansrvport string with bad uid '%s'", + value); + return -1; + } + uid = g_atoi(p); + } + + if (*end != ')') + { + LOG(LOG_LEVEL_WARNING, + "Ignoring badly-terminated chansrvport string '%s'", + value); + return -1; } + + g_snprintf(dest, dest_size, XRDP_CHANSRV_STR, uid, dnum); } else { @@ -3310,13 +3368,14 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) case MMCS_SESSION_LOGIN: { // Finished with the gateway login + // Leave the UID set in case we need it for the chansrvport + // string if (self->use_gw_login) { xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "access control check was successful"); // No reply needed for this one status = scp_send_logout_request(self->sesman_trans); - self->uid = -1; } if (status == 0 && self->use_sesman) @@ -3390,12 +3449,12 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) { char portbuff[XRDP_SOCKETS_MAXPATH]; - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, - "Connecting to chansrv"); if (self->use_sesman) { g_snprintf(portbuff, sizeof(portbuff), XRDP_CHANSRV_STR, self->uid, self->display); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Connecting to chansrv"); } else { @@ -3404,6 +3463,9 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) parse_chansrvport(cp, portbuff, sizeof(portbuff), self->uid); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Connecting to chansrv on %s", + portbuff); } xrdp_mm_update_allowed_channels(self); xrdp_mm_chansrv_connect(self, portbuff); @@ -3496,9 +3558,10 @@ xrdp_mm_get_wait_objs(struct xrdp_mm *self, { if (xrdp_region_not_empty(self->wm->screen_dirty_region)) { - int now = g_time3(); - int next_screen_draw_time = self->wm->last_screen_draw_time + - MIN_MS_BETWEEN_FRAMES; + unsigned int now = g_get_elapsed_ms(); + unsigned int next_screen_draw_time = + self->wm->last_screen_draw_time + + MIN_MS_BETWEEN_FRAMES; int diff = next_screen_draw_time - now; int ltimeout = *timeout; diff = MAX(diff, MIN_MS_TO_WAIT_FOR_MORE_UPDATES); @@ -3888,7 +3951,7 @@ xrdp_mm_check_wait_objs(struct xrdp_mm *self) { if (xrdp_region_not_empty(self->wm->screen_dirty_region)) { - int now = g_time3(); + unsigned int now = g_get_elapsed_ms(); int diff = now - self->wm->last_screen_draw_time; LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_mm_check_wait_objs: not empty diff %d", diff); if ((diff < 0) || (diff >= 40)) diff --git a/xrdp/xrdp_tconfig.c b/xrdp/xrdp_tconfig.c index 744cc3d8f3..5fc475bee9 100644 --- a/xrdp/xrdp_tconfig.c +++ b/xrdp/xrdp_tconfig.c @@ -47,6 +47,7 @@ #define X264_DEFAULT_PROFILE "main" #define X264_DEFAULT_FPS_NUM 24 #define X264_DEFAULT_FPS_DEN 1 +#define X264_DEFAULT_THREADS 1 /* not to exhaust CPU threads for 1 user */ const char * tconfig_codec_order_to_str( @@ -67,6 +68,7 @@ tconfig_codec_order_to_str( if (p > 0) { buff[p++] = ','; + buff[p++] = ' '; } switch (codec_order->codecs[i]) @@ -96,6 +98,95 @@ tconfig_codec_order_to_str( return buff; } +static int +tconfig_load_gfx_openh264_ct(toml_table_t *tfile, const int connection_type, + struct xrdp_tconfig_gfx_openh264_param *param) +{ + TCLOG(LOG_LEVEL_TRACE, "[OpenH264]"); + + if (connection_type > NUM_CONNECTION_TYPES) + { + TCLOG(LOG_LEVEL_ERROR, "[OpenH264] Invalid connection type is given"); + return 1; + } + + toml_table_t *oh264 = toml_table_in(tfile, "OpenH264"); + if (!oh264) + { + TCLOG(LOG_LEVEL_WARNING, "[OpenH264] OpenH264 params are not defined"); + return 1; + } + + toml_table_t *oh264_ct = + toml_table_in(oh264, rdpbcgr_connection_type_names[connection_type]); + toml_datum_t datum; + + if (!oh264_ct) + { + TCLOG(LOG_LEVEL_WARNING, "OpenH264 params for connection type [%s] is not defined", + rdpbcgr_connection_type_names[connection_type]); + return 1; + } + + /* EnableFrameSkip */ + datum = toml_bool_in(oh264_ct, "EnableFrameSkip"); + if (datum.ok) + { + param[connection_type].EnableFrameSkip = datum.u.b; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "[OpenH264.%s] EnableFrameSkip is not set, adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].EnableFrameSkip = 0; + } + + /* TargetBitrate */ + datum = toml_int_in(oh264_ct, "TargetBitrate"); + if (datum.ok) + { + param[connection_type].TargetBitrate = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "[OpenH264.%s] TargetBitrate is not set, adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].TargetBitrate = 0; + } + + /* MaxBitrate */ + datum = toml_int_in(oh264_ct, "MaxBitrate"); + if (datum.ok) + { + param[connection_type].MaxBitrate = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "[OpenH264.%s] MaxBitrate is not set, adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].MaxBitrate = 0; + } + + /* MaxFrameRate */ + datum = toml_double_in(oh264_ct, "MaxFrameRate"); + if (datum.ok) + { + param[connection_type].MaxFrameRate = (float)datum.u.d; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "[OpenH264.%s] MaxFrameRate is not set, adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].MaxFrameRate = 0; + } + + return 0; +} + static int tconfig_load_gfx_x264_ct(toml_table_t *tfile, const int connection_type, struct xrdp_tconfig_gfx_x264_param *param) @@ -244,6 +335,76 @@ tconfig_load_gfx_x264_ct(toml_table_t *tfile, const int connection_type, param[connection_type].fps_den = X264_DEFAULT_FPS_DEN; } + /* threads */ + datum = toml_int_in(x264_ct, "threads"); + if (datum.ok) + { + if (datum.u.i >= 0) + { + param[connection_type].threads = datum.u.i; + } + else + { + TCLOG(LOG_LEVEL_WARNING, + "[x264.%s] an invalid value (< 0) is specified for threads, " + "adopting the default value [%d]", + rdpbcgr_connection_type_names[connection_type], + X264_DEFAULT_THREADS); + param[connection_type].threads = X264_DEFAULT_THREADS; + } + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "[x264.%s] threads is not set, adopting the default value [%d]", + rdpbcgr_connection_type_names[connection_type], + X264_DEFAULT_THREADS); + param[connection_type].threads = X264_DEFAULT_THREADS; + } + + return 0; +} + +static int tconfig_load_gfx_h264_encoder(toml_table_t *tfile, struct xrdp_tconfig_gfx *config) +{ + TCLOG(LOG_LEVEL_TRACE, "[codec]"); + + toml_table_t *codec; + int valid_encoder_found = 0; + + if ((codec = toml_table_in(tfile, "codec")) != NULL) + { + toml_datum_t h264_encoder = toml_string_in(codec, "h264_encoder"); + + if (h264_encoder.ok) + { + if (g_strcasecmp(h264_encoder.u.s, "x264") == 0) + { + TCLOG(LOG_LEVEL_DEBUG, "[codec] h264_encoder = x264"); + valid_encoder_found = 1; + config->h264_encoder = XTC_H264_X264; + } + if (g_strcasecmp(h264_encoder.u.s, "OpenH264") == 0) + { + TCLOG(LOG_LEVEL_DEBUG, "[codec] h264_encoder = OpenH264"); + valid_encoder_found = 1; + config->h264_encoder = XTC_H264_OPENH264; + } + + free(h264_encoder.u.s); + } + } + + if (valid_encoder_found == 0) + { + TCLOG(LOG_LEVEL_WARNING, "[codec] could not get valid H.264 encoder, " + "using default \"x264\""); + + /* default to x264 */ + config->h264_encoder = XTC_H264_X264; + return 1; + } + return 0; } @@ -393,19 +554,20 @@ tconfig_load_gfx(const char *filename, struct xrdp_tconfig_gfx *config) /* Load GFX codec order */ tconfig_load_gfx_order(tfile, config); + /* Load H.264 encoder */ + tconfig_load_gfx_h264_encoder(tfile, config); /* H.264 configuration */ if (codec_enabled(&config->codec, XTC_H264)) { /* First of all, read the default params */ - if (tconfig_load_gfx_x264_ct(tfile, 0, config->x264_param) != 0) - { - /* We can't read the H.264 defaults. Disable H.264 */ - LOG(LOG_LEVEL_WARNING, "H.264 support will be disabled"); - disable_codec(&config->codec, XTC_H264); - rv = 1; - } - else + int x264_loaded; + int oh264_loaded; + + x264_loaded = tconfig_load_gfx_x264_ct(tfile, 0, config->x264_param); + oh264_loaded = tconfig_load_gfx_openh264_ct(tfile, 0, config->openh264_param); + + if (x264_loaded == 0) { /* Copy default params to other connection types, and * then override them */ @@ -416,6 +578,36 @@ tconfig_load_gfx(const char *filename, struct xrdp_tconfig_gfx *config) tconfig_load_gfx_x264_ct(tfile, ct, config->x264_param); } } + + if (oh264_loaded == 0) + { + /* Copy default params to other connection types, and + * then override them */ + for (int ct = CONNECTION_TYPE_MODEM; ct < NUM_CONNECTION_TYPES; + ct++) + { + config->openh264_param[ct] = config->openh264_param[0]; + tconfig_load_gfx_openh264_ct(tfile, ct, config->openh264_param); + } + } + + if (x264_loaded != 0 && config->h264_encoder == XTC_H264_X264) + { + /* We can't get x264 defaults. Disable H.264. */ + TCLOG(LOG_LEVEL_WARNING, "x264 is selected as H.264 encoder but " + "cannot load default config for x264, disabling H.264"); + disable_codec(&config->codec, XTC_H264); + rv = 1; + } + + if (oh264_loaded != 0 && config->h264_encoder == XTC_H264_OPENH264) + { + /* We can't get OpenH264 defaults. Disable H.264. */ + TCLOG(LOG_LEVEL_WARNING, "OpenH264 is selected as H.264 encoder but " + "cannot load default config for OpenH264, disabling H.264"); + disable_codec(&config->codec, XTC_H264); + rv = 1; + } } toml_free(tfile); diff --git a/xrdp/xrdp_tconfig.h b/xrdp/xrdp_tconfig.h index 649d649028..2f759fee7d 100644 --- a/xrdp/xrdp_tconfig.h +++ b/xrdp/xrdp_tconfig.h @@ -26,6 +26,8 @@ #ifndef _XRDP_TCONFIG_H_ #define _XRDP_TCONFIG_H_ +#include "arch.h" + /* The number of connection types in MS-RDPBCGR 2.2.1.3.2 */ #define NUM_CONNECTION_TYPES 7 #define GFX_CONF XRDP_CFG_PATH "/gfx.toml" @@ -40,6 +42,15 @@ struct xrdp_tconfig_gfx_x264_param int vbv_buffer_size; int fps_num; int fps_den; + int threads; +}; + +struct xrdp_tconfig_gfx_openh264_param +{ + bool_t EnableFrameSkip; + int TargetBitrate; + int MaxBitrate; + float MaxFrameRate; }; enum xrdp_tconfig_codecs @@ -48,6 +59,12 @@ enum xrdp_tconfig_codecs XTC_RFX }; +enum xrdp_tconfig_h264_encoders +{ + XTC_H264_X264, + XTC_H264_OPENH264 +}; + struct xrdp_tconfig_gfx_codec_order { enum xrdp_tconfig_codecs codecs[2]; @@ -57,8 +74,11 @@ struct xrdp_tconfig_gfx_codec_order struct xrdp_tconfig_gfx { struct xrdp_tconfig_gfx_codec_order codec; + enum xrdp_tconfig_h264_encoders h264_encoder; /* store x264 parameters for each connection type */ struct xrdp_tconfig_gfx_x264_param x264_param[NUM_CONNECTION_TYPES]; + struct xrdp_tconfig_gfx_openh264_param + openh264_param[NUM_CONNECTION_TYPES]; }; static const char *const rdpbcgr_connection_type_names[] = diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index c5c80975ef..db8e1cb57c 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -444,6 +444,10 @@ struct xrdp_mm enum xrdp_egfx_flags egfx_flags; int gfx_delay_autologin; int mod_uses_wm_screen_for_gfx; + /* Whether a working h.264 library is loaded. + * We check this at run-time, so that we can fall-back to GFX if + * the H.264 library is installed incorrectly */ + int libh264_loaded; /* != 0 => H.264 can be used */ /* Resize on-the-fly control */ struct display_control_monitor_layout_data *resize_data; struct list *resize_queue; @@ -462,6 +466,9 @@ struct xrdp_key_info struct xrdp_keymap { + // Are the caps lock maps populated? + int caps_lock_supported; + // These arrays are indexed by a return from scancode_to_index() struct xrdp_key_info keys_noshift[SCANCODE_MAX_INDEX + 1]; struct xrdp_key_info keys_shift[SCANCODE_MAX_INDEX + 1]; @@ -586,7 +593,7 @@ struct xrdp_wm struct xrdp_tconfig_gfx *gfx_config; struct xrdp_region *screen_dirty_region; - int last_screen_draw_time; + unsigned int last_screen_draw_time; }; /* rdp process */ diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c index 1efffc1507..586901b489 100644 --- a/xrdp/xrdp_wm.c +++ b/xrdp/xrdp_wm.c @@ -110,7 +110,19 @@ xrdp_wm_create(struct xrdp_process *owner, self->log->auto_free = 1; self->mm = xrdp_mm_create(self); /* this will use built in keymap or load from file */ - get_keymaps(self->session->client_info->keylayout, &(self->keymap)); + if (client_info->xrdp_keyboard_overrides.layout > 0 && + client_info->xrdp_keyboard_overrides.layout != client_info->keylayout) + { + LOG(LOG_LEVEL_INFO, "overrode keylayout %08X with %08X", + client_info->keylayout, + client_info->xrdp_keyboard_overrides.layout); + get_keymaps(client_info->xrdp_keyboard_overrides.layout, + &(self->keymap)); + } + else + { + get_keymaps(client_info->keylayout, &(self->keymap)); + } xrdp_wm_set_login_state(self, WMLS_RESET); self->target_surface = self->screen; self->current_surface_index = 0xffff; /* screen */ @@ -1261,7 +1273,7 @@ xrdp_wm_clear_popup(struct xrdp_wm *self) //struct xrdp_bitmap* b; //b = 0; - if (self->popup_wnd != 0) + if (self->popup_wnd != NULL) { //b = self->popup_wnd->popped_from; i = list_index_of(self->screen->child_list, (long)self->popup_wnd); @@ -1270,6 +1282,7 @@ xrdp_wm_clear_popup(struct xrdp_wm *self) self->popup_wnd->width, self->popup_wnd->height); xrdp_bitmap_invalidate(self->screen, &rect); xrdp_bitmap_delete(self->popup_wnd); + self->popup_wnd = NULL; } //xrdp_wm_set_focused(self, b->parent); diff --git a/xrdpapi/xrdpapi.c b/xrdpapi/xrdpapi.c index 4288b3552e..b8ce4ff244 100644 --- a/xrdpapi/xrdpapi.c +++ b/xrdpapi/xrdpapi.c @@ -55,6 +55,19 @@ mysend(int sck, const void *adata, int bytes); static int myrecv(int sck, void *adata, int bytes); +static void +free_wts(struct wts_obj *wts) +{ + if (wts != NULL) + { + if (wts->fd >= 0) + { + close(wts->fd); + } + free(wts); + } +} + /* * Opens a handle to the server end of a specified virtual channel - this * call is deprecated - use WTSVirtualChannelOpenEx() instead @@ -116,7 +129,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (wts->display_num < 0) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: fatal error; invalid DISPLAY"); - free(wts); + free_wts(wts); return NULL; } @@ -124,7 +137,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: socket failed"); - free(wts); + free_wts(wts); return NULL; } @@ -155,7 +168,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, else { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect failed"); - free(wts); + free_wts(wts); return NULL; } } @@ -164,7 +177,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (!can_send(wts->fd, 500, 1)) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_send failed"); - free(wts); + free_wts(wts); return NULL; } @@ -179,7 +192,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (connect_data == NULL) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: calloc failed"); - free(wts); + free_wts(wts); return NULL; } @@ -208,7 +221,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (mysend(wts->fd, connect_data, bytes) != bytes) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: mysend failed"); - free(wts); + free_wts(wts); return NULL; } LOG_DEVEL(LOG_LEVEL_DEBUG, "WTSVirtualChannelOpenEx: sent ok"); @@ -216,7 +229,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (!can_recv(wts->fd, 500, 1)) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_recv failed"); - free(wts); + free_wts(wts); return NULL; } @@ -224,7 +237,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, if (myrecv(wts->fd, connect_data, 4) != 4) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: myrecv failed"); - free(wts); + free_wts(wts); return NULL; } @@ -232,7 +245,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, (connect_data[2] != 0) || (connect_data[3] != 0)) { LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect_data not ok"); - free(wts); + free_wts(wts); return NULL; } @@ -406,21 +419,14 @@ WTSVirtualChannelRead(void *hChannelHandle, unsigned int TimeOut, int WTSVirtualChannelClose(void *hChannelHandle) { - struct wts_obj *wts; - - wts = (struct wts_obj *)hChannelHandle; + struct wts_obj *wts = (struct wts_obj *)hChannelHandle; if (wts == NULL) { return 0; } - if (wts->fd != -1) - { - close(wts->fd); - } - - free(wts); + free_wts(wts); return 1; } diff --git a/xup/Makefile.am b/xup/Makefile.am index b51a7fb3ba..d073c0d0b1 100644 --- a/xup/Makefile.am +++ b/xup/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = \ - -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_CFG_PATH=\"${sysconfdir}/${sysconfsubdir}\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ diff --git a/xup/xup.c b/xup/xup.c index 71ced6001c..ddc2c66cd6 100644 --- a/xup/xup.c +++ b/xup/xup.c @@ -146,6 +146,72 @@ lib_data_in(struct trans *trans) return 0; } +/******************************************************************************/ +/* + * Wait for module caps message from Xorg module + * + * This routine waits for the Xorg module to send a caps message. + * + * We use this to check the caps are compatible with us before we + * go for a fuull-on connect + */ +static int +wait_for_module_caps_message(struct mod *mod) +{ + int robjs_count; + intptr_t robjs[10]; + + mod->caps_processing_status = E_CAPS_NOT_PROCESSED; + + while (mod->caps_processing_status == E_CAPS_NOT_PROCESSED) + { + robjs_count = 0; + if (trans_get_wait_objs(mod->trans, robjs, &robjs_count) != 0) + { + LOG(LOG_LEVEL_ERROR, "Xorg module has dropped connection"); + return 1; + } + + // We don't need a big timeout here, as all the module has to do is + // turn around the version message. + int status = g_obj_wait(robjs, robjs_count, 0, 0, 3 * 1000); + + if (status < 0) + { + LOG(LOG_LEVEL_ERROR, "No response from Xorg module before timeout"); + return 1; + } + + (void)trans_check_wait_objs(mod->trans); + } + + return (mod->caps_processing_status == E_CAPS_OK) ? 0 : 1; +} + +/******************************************************************************/ +/* return error */ +static int +lib_send_client_info(struct mod *mod) +{ + struct stream *s; + int len; + + LOG_DEVEL(LOG_LEVEL_TRACE, "lib_send_client_info:"); + make_stream(s); + init_stream(s, 8192); + s_push_layer(s, iso_hdr, 4); + out_uint16_le(s, 104); + g_memcpy(s->p, &(mod->client_info), sizeof(mod->client_info)); + s->p += sizeof(mod->client_info); + s_mark_end(s); + len = (int)(s->end - s->data); + s_pop_layer(s, iso_hdr); + out_uint32_le(s, len); + lib_send_copy(mod, s); + free_stream(s); + return 0; +} + /******************************************************************************/ /* return error */ static int @@ -191,6 +257,19 @@ lib_mod_connect(struct mod *mod) LOG(LOG_LEVEL_INFO, "XKB rules '%s' will be used by the module", mod->client_info.xkb_rules); + if (mod->client_info.h264_frame_interval <= 0) + { + mod->client_info.h264_frame_interval = DEFAULT_H264_FRAME_INTERVAL; + } + if (mod->client_info.rfx_frame_interval <= 0) + { + mod->client_info.rfx_frame_interval = DEFAULT_RFX_FRAME_INTERVAL; + } + if (mod->client_info.normal_frame_interval <= 0) + { + mod->client_info.normal_frame_interval = DEFAULT_NORMAL_FRAME_INTERVAL; + } + make_stream(s); g_sprintf(con_port, "%s", mod->port); @@ -220,31 +299,47 @@ lib_mod_connect(struct mod *mod) free_stream(s); return 1; } + + // Set the transport up mod->trans->si = mod->si; mod->trans->my_source = XRDP_SOURCE_MOD; mod->trans->is_term = mod->server_is_term; + mod->trans->trans_data_in = lib_data_in; + mod->trans->header_size = 8; + mod->trans->callback_data = mod; + mod->trans->no_stream_init_on_data_in = 1; + mod->trans->extra_flags = 1; /* Give the X server a bit of time to start */ - if (trans_connect(mod->trans, mod->ip, con_port, 30 * 1000) == 0) + error = trans_connect(mod->trans, mod->ip, con_port, 30 * 1000); + if (error == 0) { LOG_DEVEL(LOG_LEVEL_INFO, "lib_mod_connect: connected to Xserver " "(Xorg) sck %lld", (long long) (mod->trans->sck)); + if (socket_mode == TRANS_MODE_UNIX) + { + lib_mod_log_peer(mod); + } } else { mod->server_msg(mod, "connection problem, giving up", 0); - error = 1; } - if (error == 0 && socket_mode == TRANS_MODE_UNIX) + if (error == 0) { - lib_mod_log_peer(mod); + error = send_server_version_message(mod, s); } if (error == 0) { - error = send_server_version_message(mod, s); + error = wait_for_module_caps_message(mod); + } + + if (error == 0) + { + error = lib_send_client_info(mod); } if (error == 0) @@ -259,21 +354,14 @@ lib_mod_connect(struct mod *mod) { trans_delete(mod->trans); mod->trans = 0; - mod->server_msg(mod, "some problem", 0); - return 1; + mod->server_msg(mod, "Error connecting to Xorg - check log", 0); } else { mod->server_msg(mod, "connected ok", 0); - mod->trans->trans_data_in = lib_data_in; - mod->trans->header_size = 8; - mod->trans->callback_data = mod; - mod->trans->no_stream_init_on_data_in = 1; - mod->trans->extra_flags = 1; } - LOG_DEVEL(LOG_LEVEL_TRACE, "out lib_mod_connect"); - return 0; + return error; } /******************************************************************************/ @@ -1708,30 +1796,6 @@ lib_mod_process_orders(struct mod *mod, int type, struct stream *s) return rv; } -/******************************************************************************/ -/* return error */ -static int -lib_send_client_info(struct mod *mod) -{ - struct stream *s; - int len; - - LOG_DEVEL(LOG_LEVEL_TRACE, "lib_send_client_info:"); - make_stream(s); - init_stream(s, 8192); - s_push_layer(s, iso_hdr, 4); - out_uint16_le(s, 104); - g_memcpy(s->p, &(mod->client_info), sizeof(mod->client_info)); - s->p += sizeof(mod->client_info); - s_mark_end(s); - len = (int)(s->end - s->data); - s_pop_layer(s, iso_hdr); - out_uint32_le(s, len); - lib_send_copy(mod, s); - free_stream(s); - return 0; -} - /******************************************************************************/ /* return error */ static int @@ -1743,6 +1807,7 @@ lib_mod_process_message(struct mod *mod, struct stream *s) int len; int type; char *phold; + int version; int width; int height; @@ -1769,6 +1834,7 @@ lib_mod_process_message(struct mod *mod, struct stream *s) } else if (type == 2) /* caps */ { + mod->caps_processing_status = E_CAPS_OK; /* Assume all OK */ LOG_DEVEL(LOG_LEVEL_TRACE, "lib_mod_process_message: type 2 len %d", len); for (index = 0; index < num_orders; index++) @@ -1779,6 +1845,20 @@ lib_mod_process_message(struct mod *mod, struct stream *s) switch (type) { + case 100: + in_uint32_le(s, version); + if (version != CLIENT_INFO_CURRENT_VERSION) + { + char msg[128]; + g_snprintf(msg, sizeof(msg), + "Xorg module has version %d, expected %d", + version, CLIENT_INFO_CURRENT_VERSION); + LOG(LOG_LEVEL_ERROR, "%s", msg); + mod->server_msg(mod, msg, 0); + mod->caps_processing_status = E_CAPS_NOT_OK; + } + break; + default: LOG_DEVEL(LOG_LEVEL_TRACE, "lib_mod_process_message: unknown" @@ -1788,7 +1868,6 @@ lib_mod_process_message(struct mod *mod, struct stream *s) } s->p = phold + len; } - lib_send_client_info(mod); } else if (type == 3) /* order list with len after type */ { @@ -1887,6 +1966,18 @@ lib_mod_set_param(struct mod *mod, const char *name, const char *value) { g_snprintf(mod->keycode_set, sizeof(mod->keycode_set), "%s", value); } + else if (g_strcasecmp(name, "h264_frame_interval") == 0) + { + mod->client_info.h264_frame_interval = g_atoi(value); + } + else if (g_strcasecmp(name, "rfx_frame_interval") == 0) + { + mod->client_info.rfx_frame_interval = g_atoi(value); + } + else if (g_strcasecmp(name, "normal_frame_interval") == 0) + { + mod->client_info.normal_frame_interval = g_atoi(value); + } else if (g_strcasecmp(name, "client_info") == 0) { g_memcpy(&(mod->client_info), value, sizeof(mod->client_info)); diff --git a/xup/xup.h b/xup/xup.h index 37aa67dc6a..6486be0466 100644 --- a/xup/xup.h +++ b/xup/xup.h @@ -21,6 +21,17 @@ #ifndef XUP_H #define XUP_H +/** + * Enum for the states used to process a + * capabilities message from the Xorg module + */ +enum caps_processing_status +{ + E_CAPS_NOT_PROCESSED, ///< Capabilities mesage from module not processed + E_CAPS_OK, ///< Capabilities are OK + E_CAPS_NOT_OK ///< Capabilities are not OK +}; + /* include other h files */ #include "arch.h" #include "parse.h" @@ -202,6 +213,7 @@ struct mod char *screen_shmem_pixels; struct trans *trans; char keycode_set[32]; + enum caps_processing_status caps_processing_status; }; #endif // XUP_H