4417646cda
The sys/signal.h is essentially a redirect to signal.h, without further logic. However when using musl libc, a warning (which is treated as an error) is also emitted about this: | /usr/include/sys/signal.h:1:2: error: #warning redirecting incorrect #include <sys/signal.h> to <signal.h> [-Werror=cpp] | 1 | #warning redirecting incorrect #include <sys/signal.h> to <signal.h> Remove sys/signal.h header to solve the problem - the signal.h header is already included in this file. Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
269 lines
7.0 KiB
C
269 lines
7.0 KiB
C
#include <X11/extensions/Xrandr.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
|
|
#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
|
|
#define ALARM_WAIT 30
|
|
|
|
/*****************************************************************************/
|
|
static void
|
|
alarm_handler(int signal_num)
|
|
{
|
|
/* Avoid printf() in signal handler (see signal-safety(7))
|
|
*
|
|
* Prefix the message with a newline in case another message
|
|
* has been partly output */
|
|
const char msg[] = "\n<E>Timed 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 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;
|
|
|
|
// 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("<D>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("<D>Socket '%s' open succeeded.\n", sock_name);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
printf("<D>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("<D>Opening display '%s'. Attempt %u of %u\n", display, n, wait);
|
|
if ((dpy = XOpenDisplay(display)) != NULL)
|
|
{
|
|
printf("<D>Opened display %s\n", display);
|
|
break;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Wait for the RandR extension (if in use) to be available
|
|
*
|
|
* @param dpy Display
|
|
* @return 0 if/when outputs are available, 1 otherwise
|
|
*/
|
|
static int
|
|
wait_for_r_and_r(Display *dpy)
|
|
{
|
|
int error_base = 0;
|
|
int event_base = 0;
|
|
unsigned int outputs = 0;
|
|
unsigned int wait = ATTEMPTS;
|
|
unsigned int n;
|
|
|
|
XRRScreenResources *res = NULL;
|
|
|
|
if (!XRRQueryExtension(dpy, &event_base, &error_base))
|
|
{
|
|
printf("<I>RandR not supported on display %s\n",
|
|
DisplayString(dpy));
|
|
return 0;
|
|
}
|
|
|
|
for (n = 1; n <= wait; ++n)
|
|
{
|
|
printf("<D>Waiting for outputs. Attempt %u of %u\n", n, wait);
|
|
res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
|
|
if (res != NULL)
|
|
{
|
|
if (res->noutput > 0)
|
|
{
|
|
outputs = res->noutput;
|
|
}
|
|
XRRFreeScreenResources(res);
|
|
}
|
|
|
|
if (outputs > 0)
|
|
{
|
|
printf("<D>Display %s ready with %u RandR outputs\n",
|
|
DisplayString(dpy), outputs);
|
|
return 0;
|
|
}
|
|
g_sleep(1000);
|
|
}
|
|
|
|
printf("<E>Unable to find any RandR outputs\n");
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static void
|
|
usage(const char *argv0, int status)
|
|
{
|
|
printf("Usage: %s -d display\n", argv0);
|
|
exit(status);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *display_name = NULL;
|
|
int opt;
|
|
int status = XW_STATUS_MISC_ERROR;
|
|
Display *dpy = NULL;
|
|
|
|
/* Disable stdout buffering so any messages are passed immediately
|
|
* to sesman */
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
while ((opt = getopt(argc, argv, "d:")) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'd':
|
|
display_name = optarg;
|
|
break;
|
|
default: /* '?' */
|
|
usage(argv[0], status);
|
|
}
|
|
}
|
|
|
|
if (!display_name)
|
|
{
|
|
usage(argv[0], status);
|
|
}
|
|
|
|
g_set_alarm(alarm_handler, ALARM_WAIT);
|
|
|
|
dpy = open_display(display_name);
|
|
if (!dpy)
|
|
{
|
|
printf("<E>Unable to open display %s\n", display_name);
|
|
status = XW_STATUS_FAILED_TO_START;
|
|
}
|
|
else
|
|
{
|
|
if (wait_for_r_and_r(dpy) == 0)
|
|
{
|
|
status = XW_STATUS_OK;
|
|
}
|
|
XCloseDisplay(dpy);
|
|
}
|
|
|
|
return status;
|
|
}
|