diff --git a/Makefile.am b/Makefile.am index d7a5d84626..59479f05ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,6 +58,7 @@ SUBDIRS = \ xrdp \ fontutils \ keygen \ + waitforx \ docs \ instfiles \ genkeymap \ diff --git a/common/os_calls.c b/common/os_calls.c index 3ee3bb324e..19b28c0d84 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -2808,6 +2808,22 @@ g_execlp3(const char *a1, const char *a2, const char *a3) #endif } +/*****************************************************************************/ +/* does not work in win32 */ +unsigned int +g_set_alarm(void (*func)(int), unsigned int secs) +{ +#if defined(_WIN32) + return 0; +#else + /* Cancel any previous alarm to prevent a race */ + unsigned int rv = alarm(0); + signal(SIGALRM, func); + (void)alarm(secs); + return rv; +#endif +} + /*****************************************************************************/ /* does not work in win32 */ void diff --git a/common/os_calls.h b/common/os_calls.h index ba4e3bcde6..aedcfc24e1 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -204,6 +204,7 @@ char *g_get_strerror(void); int g_get_errno(void); int g_execvp(const char *p1, char *args[]); int g_execlp3(const char *a1, const char *a2, const char *a3); +unsigned int g_set_alarm(void (*func)(int), unsigned int secs); void g_signal_child_stop(void (*func)(int)); void g_signal_segfault(void (*func)(int)); void g_signal_hang_up(void (*func)(int)); diff --git a/configure.ac b/configure.ac index 040f0ad02d..63c1c7098c 100644 --- a/configure.ac +++ b/configure.ac @@ -575,6 +575,7 @@ AC_CONFIG_FILES([ instfiles/pulse/Makefile instfiles/rc.d/Makefile keygen/Makefile + waitforx/Makefile libipm/Makefile libxrdp/Makefile Makefile diff --git a/sesman/Makefile.am b/sesman/Makefile.am index 78bb3d4d40..61bd24b4cd 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -DXRDP_SYSCONF_PATH=\"${sysconfdir}\" \ -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ -DXRDP_SBIN_PATH=\"${sbindir}\" \ + -DXRDP_BIN_PATH=\"${bindir}\" \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ -DXRDP_SOCKET_PATH=\"${socketdir}\" \ @@ -34,7 +35,9 @@ xrdp_sesman_SOURCES = \ sig.c \ sig.h \ xauth.c \ - xauth.h + xauth.h \ + xwait.c \ + xwait.h # Possible authentication modules # See https://www.gnu.org/software/automake/manual/html_node/Conditional-Sources.html diff --git a/sesman/session.c b/sesman/session.c index b5e8da9b96..77af95cb2e 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -49,6 +49,7 @@ #include "sesman.h" #include "string_calls.h" #include "xauth.h" +#include "xwait.h" #include "xrdp_sockets.h" #ifndef PR_SET_NO_NEW_PRIVS @@ -285,42 +286,6 @@ x_server_running_check_ports(int display) return x_running; } -/******************************************************************************/ -/** - * - * @brief checks if there's a server running on a display - * @param display the display to check - * @return 0 if there isn't a display running, nonzero otherwise - * - */ -static int -x_server_running(int display) -{ - char text[256]; - int x_running; - - g_sprintf(text, "/tmp/.X11-unix/X%d", 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); - x_running = g_file_exist(text); - } - - if (x_running) - { - LOG(LOG_LEVEL_INFO, "Found X server running at %s", text); - } - else - { - LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - } - - return x_running; -} - /******************************************************************************/ /* called with the main thread returns boolean */ @@ -375,37 +340,6 @@ session_get_avail_display_from_chain(void) return 0; } -/******************************************************************************/ -static int -wait_for_xserver(int display) -{ - int i; - - /* give X a bit to start */ - /* wait up to 10 secs for x server to start */ - i = 0; - - LOG(LOG_LEVEL_DEBUG, "Waiting for X server to start on display %d", display); - - while (!x_server_running(display)) - { - i++; - - if (i > 40) - { - LOG(LOG_LEVEL_WARNING, - "Timed out waiting for X server on display %d to startup", - display); - break; - } - - g_sleep(250); - } - - return 0; -} - -/******************************************************************************/ static int session_start_chansrv(int uid, int display) { @@ -655,13 +589,12 @@ session_start(struct auth_info *auth_info, } else if (window_manager_pid == 0) { - wait_for_xserver(display); env_set_user(s->uid, 0, display, g_cfg->env_names, g_cfg->env_values); - if (x_server_running(display)) + if (wait_for_xserver(display)) { auth_set_env(auth_info); if (s->directory != 0) @@ -912,7 +845,6 @@ session_start(struct auth_info *auth_info, struct exit_status xserver_exit_status; struct exit_status chansrv_exit_status; - wait_for_xserver(display); chansrv_pid = session_start_chansrv(s->uid, display); LOG(LOG_LEVEL_INFO, diff --git a/sesman/xwait.c b/sesman/xwait.c new file mode 100644 index 0000000000..3e992079f0 --- /dev/null +++ b/sesman/xwait.c @@ -0,0 +1,47 @@ +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "log.h" +#include "os_calls.h" +#include "string_calls.h" +#include "xwait.h" + +#include +#include + +/******************************************************************************/ +int +wait_for_xserver(int display) +{ + FILE *dp = NULL; + int ret = 0; + char buffer[100]; + char exe_cmd[262]; + + LOG(LOG_LEVEL_DEBUG, "Waiting for X server to start on display %d", display); + + g_snprintf(exe_cmd, sizeof(exe_cmd), "%s/xrdp-waitforx", XRDP_BIN_PATH); + dp = popen(exe_cmd, "r"); + if (dp == NULL) + { + LOG(LOG_LEVEL_ERROR, "Unable to launch xrdp-waitforx"); + return 1; + } + + while (fgets(buffer, 100, dp)) + { + g_strtrim(buffer, 2); + LOG(LOG_LEVEL_DEBUG, "%s", buffer); + } + + ret = pclose(dp); + if (ret != 0) + { + LOG(LOG_LEVEL_ERROR, "An error occurred while running xrdp-waitforx"); + return 0; + } + + + return 1; +} diff --git a/sesman/xwait.h b/sesman/xwait.h new file mode 100644 index 0000000000..d8beb4a50d --- /dev/null +++ b/sesman/xwait.h @@ -0,0 +1,12 @@ +#ifndef XWAIT_H +#define XWAIT_H +/** + * + * @brief waits for X to start + * @param display number + * @return 0 on error, 1 if X has outputs + * + */ +int +wait_for_xserver(int display); +#endif diff --git a/waitforx/Makefile.am b/waitforx/Makefile.am new file mode 100644 index 0000000000..359361c3a5 --- /dev/null +++ b/waitforx/Makefile.am @@ -0,0 +1,10 @@ +bin_PROGRAMS = \ + xrdp-waitforx + +AM_LDFLAGS = -lX11 -lXrandr +AM_CFLAGS = -I$(top_srcdir)/common + +xrdp_waitforx_SOURCES = waitforx.c + +xrdp_waitforx_LDADD = \ + $(top_builddir)/common/libcommon.la diff --git a/waitforx/waitforx.c b/waitforx/waitforx.c new file mode 100644 index 0000000000..c60b114c35 --- /dev/null +++ b/waitforx/waitforx.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +#include "config_ac.h" +#include "os_calls.h" +#include "string_calls.h" + +#define ATTEMPTS 10 +#define ALARM_WAIT 30 + +void +alarm_handler(int signal_num) +{ + /* Avoid printf() in signal handler (see signal-safety(7)) */ + const char msg[] = "Timed out waiting for RandR outputs\n"; + g_file_write(1, msg, g_strlen(msg)); + exit(1); +} + +int +main(int argc, char **argv) +{ + char *display = NULL; + int error_base = 0; + int event_base = 0; + int n = 0; + int outputs = 0; + int wait = ATTEMPTS; + + Display *dpy = NULL; + XRRScreenResources *res = NULL; + + display = getenv("DISPLAY"); + + g_set_alarm(alarm_handler, ALARM_WAIT); + + if (!display) + { + printf("DISPLAY is null"); + exit(1); + } + + for (n = 1; n <= wait; ++n) + { + dpy = XOpenDisplay(display); + printf("Opening display %s. Attempt %d of %d\n", display, n, wait); + if (dpy != NULL) + { + printf("Opened display %s\n", display); + break; + } + g_sleep(1000); + } + + if (!dpy) + { + printf("Unable to open display %s\n", display); + exit(1); + } + + if (!XRRQueryExtension(dpy, &event_base, &error_base)) + { + printf("RandR not supported on display %s", display); + } + else + { + for (n = 1; n <= wait; ++n) + { + res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); + printf("Waiting for outputs. Attempt %d of %d\n", n, wait); + if (res != NULL) + { + if (res->noutput > 0) + { + outputs = res->noutput; + XRRFreeScreenResources(res); + printf("Found %d output[s]\n", outputs); + break; + } + XRRFreeScreenResources(res); + } + g_sleep(1000); + } + + if (outputs > 0) + { + printf("display %s ready with %d outputs\n", display, res->noutput); + } + else + { + printf("Unable to find any outputs\n"); + exit(1); + } + } + + exit(0); +}