summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Vlad <marius.vlad@collabora.com>2024-04-08 15:37:42 +0300
committerMarius Vlad <marius.vlad@collabora.com>2024-04-09 11:41:36 +0300
commit18607e2245d1e9ccfdd19db894e4cfdb52def303 (patch)
treeb9a62e4ab81bd33fb6af5c996f92ad756536f57d
parent50f3305cfa8ef0713fbabdc9670e6856f7d8e69e (diff)
compositor: Add support for building Xwayland
This updates with the Weston's front-end. Bug-AGL: SPEC-5096, SPEC-5061 Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Change-Id: Id421bdd04fc6943e991cbf51e9478450411721ef
-rw-r--r--meson.build2
-rw-r--r--shared/process-util.c3
-rw-r--r--shared/process-util.h1
-rw-r--r--src/desktop.c4
-rw-r--r--src/xwayland.c408
5 files changed, 288 insertions, 130 deletions
diff --git a/meson.build b/meson.build
index 3b92cca..9b942f0 100644
--- a/meson.build
+++ b/meson.build
@@ -220,6 +220,8 @@ if get_option('xwayland')
config_h.set('BUILD_XWAYLAND', '1')
srcs_agl_compositor += 'src/xwayland.c'
+ srcs_agl_compositor += 'shared/xalloc.c'
+ srcs_agl_compositor += 'shared/process-util.c'
config_h.set_quoted('XSERVER_PATH', get_option('xwayland-path'))
endif
diff --git a/shared/process-util.c b/shared/process-util.c
index e36c647..fe895d2 100644
--- a/shared/process-util.c
+++ b/shared/process-util.c
@@ -68,7 +68,8 @@ fdstr_close_all(struct fdstr *s)
unsigned i;
for (i = 0; i < ARRAY_LENGTH(s->fds); i++) {
- close(s->fds[i]);
+ if (s->fds[i] >= 0)
+ close(s->fds[i]);
s->fds[i] = -1;
}
}
diff --git a/shared/process-util.h b/shared/process-util.h
index 05543f6..aa35c77 100644
--- a/shared/process-util.h
+++ b/shared/process-util.h
@@ -59,6 +59,7 @@ fdstr_clear_cloexec_fd1(struct fdstr *s);
void
fdstr_close_all(struct fdstr *s);
+#define FDSTR_INIT ((struct fdstr){ { 0 }, { -1, -1 }})
/**
* A container for environment variables and/or process arguments, designed to
diff --git a/src/desktop.c b/src/desktop.c
index d5b0be9..d3c1d4e 100644
--- a/src/desktop.c
+++ b/src/desktop.c
@@ -602,8 +602,8 @@ transform_handler(struct wl_listener *listener, void *data)
if (!weston_view_is_mapped(ivisurf->view))
return;
- x = ivisurf->view->geometry.x;
- y = ivisurf->view->geometry.y;
+ x = ivisurf->view->geometry.pos_offset.x;
+ y = ivisurf->view->geometry.pos_offset.y;
api->send_position(surface, x, y);
#endif
diff --git a/src/xwayland.c b/src/xwayland.c
index 8ac5edc..229ce75 100644
--- a/src/xwayland.c
+++ b/src/xwayland.c
@@ -31,194 +31,348 @@
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
+#include <fcntl.h>
#include <libweston/libweston.h>
#include <weston.h>
#include <libweston/xwayland-api.h>
#include "shared/helpers.h"
+#include "shared/process-util.h"
+#include "shared/xalloc.h"
+
+#ifdef HAVE_XWAYLAND_LISTENFD
+# define LISTEN_STR "-listenfd"
+#else
+# define LISTEN_STR "-listen"
+#endif
struct wet_xwayland {
- struct ivi_compositor *ivi_compositor;
+ struct weston_compositor *compositor;
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
- struct wl_event_source *sigusr1_source;
- struct wl_client *client;
+ struct wl_event_source *display_fd_source;
int wm_fd;
- struct weston_process process;
+ struct wet_process *process;
};
+static void
+process_destroy(struct wet_process *process, int status, bool call_cleanup)
+{
+ wl_list_remove(&process->link);
+ if (call_cleanup && process->cleanup)
+ process->cleanup(process, status, process->cleanup_data);
+ free(process->path);
+ free(process);
+}
+
static int
-handle_sigusr1(int signal_number, void *data)
+handle_display_fd(int fd, uint32_t mask, void *data)
+{
+ struct wet_xwayland *wxw = data;
+ char buf[64];
+ ssize_t n;
+
+ /* xwayland exited before being ready, don't finish initialization,
+ * the process watcher will cleanup */
+ if (!(mask & WL_EVENT_READABLE))
+ goto out;
+
+ /* Xwayland writes to the pipe twice, so if we close it too early
+ * it's possible the second write will fail and Xwayland shuts down.
+ * Make sure we read until end of line marker to avoid this. */
+ n = read(fd, buf, sizeof buf);
+ if (n < 0 && errno != EAGAIN) {
+ weston_log("read from Xwayland display_fd failed: %s\n",
+ strerror(errno));
+ goto out;
+ }
+ /* Returning 1 here means recheck and call us again if required. */
+ if (n <= 0 || (n > 0 && buf[n - 1] != '\n'))
+ return 1;
+
+ wxw->api->xserver_loaded(wxw->xwayland, wxw->wm_fd);
+
+out:
+ wl_event_source_remove(wxw->display_fd_source);
+ close(fd);
+
+ return 0;
+}
+
+
+static void
+xserver_cleanup(struct wet_process *process, int status, void *data)
{
struct wet_xwayland *wxw = data;
- /* We'd be safer if we actually had the struct
- * signalfd_siginfo from the signalfd data and could verify
- * this came from Xwayland.*/
- wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
- wl_event_source_remove(wxw->sigusr1_source);
+ /* We only have one Xwayland process active, so make sure it's the
+ * right one */
+ assert(process == wxw->process);
- return 1;
+ wxw->api->xserver_exited(wxw->xwayland);
+ wxw->process = NULL;
}
+static void
+cleanup_for_child_process() {
+ sigset_t allsigs;
-static pid_t
+ /* Put the client in a new session so it won't catch signals
+ * intended for the parent. Sharing a session can be
+ * confusing when launching weston under gdb, as the ctrl-c
+ * intended for gdb will pass to the child, and weston
+ * will cleanly shut down when the child exits.
+ */
+ setsid();
+
+ /* do not give our signal mask to the new process */
+ sigfillset(&allsigs);
+ sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
+}
+
+static struct wet_process *
+client_launch(struct weston_compositor *compositor,
+ struct custom_env *child_env,
+ int *no_cloexec_fds,
+ size_t num_no_cloexec_fds,
+ wet_process_cleanup_func_t cleanup,
+ void *cleanup_data)
+{
+ struct ivi_compositor *ivi = to_ivi_compositor(compositor);
+ struct wet_process *proc = NULL;
+ const char *fail_cloexec = "Couldn't unset CLOEXEC on child FDs";
+ const char *fail_seteuid = "Couldn't call seteuid";
+ char *fail_exec;
+ char * const *argp;
+ char * const *envp;
+ pid_t pid;
+ int err;
+ size_t i;
+ size_t written __attribute__((unused));
+
+ argp = custom_env_get_argp(child_env);
+ envp = custom_env_get_envp(child_env);
+
+ weston_log("launching '%s'\n", argp[0]);
+ str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", argp[0]);
+
+ pid = fork();
+ switch (pid) {
+ case 0:
+ cleanup_for_child_process();
+
+ /* Launch clients as the user. Do not launch clients with wrong euid. */
+ if (seteuid(getuid()) == -1) {
+ written = write(STDERR_FILENO, fail_seteuid,
+ strlen(fail_seteuid));
+ _exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_no_cloexec_fds; i++) {
+ err = os_fd_clear_cloexec(no_cloexec_fds[i]);
+ if (err < 0) {
+ written = write(STDERR_FILENO, fail_cloexec,
+ strlen(fail_cloexec));
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ execve(argp[0], argp, envp);
+
+ if (fail_exec)
+ written = write(STDERR_FILENO, fail_exec,
+ strlen(fail_exec));
+ _exit(EXIT_FAILURE);
+
+ default:
+ proc = xzalloc(sizeof(*proc));
+ proc->pid = pid;
+ proc->cleanup = cleanup;
+ proc->cleanup_data = cleanup_data;
+ proc->path = strdup(argp[0]);
+ wl_list_insert(&ivi->child_process_list, &proc->link);
+ break;
+
+ case -1:
+ weston_log("weston_client_launch: "
+ "fork failed while launching '%s': %s\n", argp[0],
+ strerror(errno));
+ break;
+ }
+
+ custom_env_fini(child_env);
+ free(fail_exec);
+ return proc;
+}
+
+static struct weston_config *
+ivi_get_config(struct weston_compositor *ec)
+{
+ struct ivi_compositor *ivi = to_ivi_compositor(ec);
+
+ return ivi->config;
+}
+
+
+static struct wl_client *
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
{
struct wet_xwayland *wxw = user_data;
- pid_t pid;
- char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12];
- int sv[2], wm[2], fd;
+ struct fdstr wayland_socket = FDSTR_INIT;
+ struct fdstr x11_abstract_socket = FDSTR_INIT;
+ struct fdstr x11_unix_socket = FDSTR_INIT;
+ struct fdstr x11_wm_socket = FDSTR_INIT;
+ struct fdstr display_pipe = FDSTR_INIT;
+
char *xserver = NULL;
- struct weston_config *config = wxw->ivi_compositor->config;
+ struct weston_config *config = ivi_get_config(wxw->compositor);
struct weston_config_section *section;
+ struct wl_client *client;
+ struct wl_event_loop *loop;
+ struct custom_env child_env;
+ int no_cloexec_fds[5];
+ size_t num_no_cloexec_fds = 0;
+ size_t written __attribute__ ((unused));
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) {
weston_log("wl connection socketpair failed\n");
- return 1;
+ goto err;
}
+ fdstr_update_str1(&wayland_socket);
+ no_cloexec_fds[num_no_cloexec_fds++] = wayland_socket.fds[1];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) {
+ if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) {
weston_log("X wm connection socketpair failed\n");
- return 1;
+ goto err;
}
+ fdstr_update_str1(&x11_wm_socket);
+ no_cloexec_fds[num_no_cloexec_fds++] = x11_wm_socket.fds[1];
- pid = fork();
- switch (pid) {
- case 0:
- /* SOCK_CLOEXEC closes both ends, so we need to unset
- * the flag on the client fd. */
- fd = dup(sv[1]);
- if (fd < 0)
- goto fail;
- snprintf(s, sizeof s, "%d", fd);
- setenv("WAYLAND_SOCKET", s, 1);
-
- fd = dup(abstract_fd);
- if (fd < 0)
- goto fail;
- snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd);
- fd = dup(unix_fd);
- if (fd < 0)
- goto fail;
- snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd);
- fd = dup(wm[1]);
- if (fd < 0)
- goto fail;
- snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd);
-
- section = weston_config_get_section(config,
- "xwayland", NULL, NULL);
- weston_config_section_get_string(section, "path",
- &xserver, XSERVER_PATH);
-
- /* Ignore SIGUSR1 in the child, which will make the X
- * server send SIGUSR1 to the parent (weston) when
- * it's done with initialization. During
- * initialization the X server will round trip and
- * block on the wayland compositor, so avoid making
- * blocking requests (like xcb_connect_to_fd) until
- * it's done with that. */
- signal(SIGUSR1, SIG_IGN);
-
- if (execl(xserver,
- xserver,
- display,
- "-rootless",
-#ifdef HAVE_XWAYLAND_LISTENFD
- "-listenfd", abstract_fd_str,
- "-listenfd", unix_fd_str,
-#else
- "-listen", abstract_fd_str,
- "-listen", unix_fd_str,
-#endif
- "-wm", wm_fd_str,
- "-terminate",
- NULL) < 0)
- weston_log("exec of '%s %s -rootless "
-#ifdef HAVE_XWAYLAND_LISTENFD
- "-listenfd %s -listenfd %s "
-#else
- "-listen %s -listen %s "
-#endif
- "-wm %s -terminate' failed: %s\n",
- xserver, display,
- abstract_fd_str, unix_fd_str, wm_fd_str,
- strerror(errno));
- fail:
- _exit(EXIT_FAILURE);
+ if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) {
+ weston_log("pipe creation for displayfd failed\n");
+ goto err;
+ }
+ fdstr_update_str1(&display_pipe);
+ no_cloexec_fds[num_no_cloexec_fds++] = display_pipe.fds[1];
- default:
- close(sv[1]);
- wxw->client = wl_client_create(wxw->ivi_compositor->compositor->wl_display, sv[0]);
+ fdstr_set_fd1(&x11_abstract_socket, abstract_fd);
+ no_cloexec_fds[num_no_cloexec_fds++] = abstract_fd;
- close(wm[1]);
- wxw->wm_fd = wm[0];
+ fdstr_set_fd1(&x11_unix_socket, unix_fd);
+ no_cloexec_fds[num_no_cloexec_fds++] = unix_fd;
- wxw->process.pid = pid;
- wl_list_insert(&wxw->ivi_compositor->child_process_list,
- &wxw->process.link);
- break;
+ assert(num_no_cloexec_fds <= ARRAY_LENGTH(no_cloexec_fds));
- case -1:
- weston_log("Failed to fork to spawn xserver process\n");
- break;
+ section = weston_config_get_section(config, "xwayland", NULL, NULL);
+ weston_config_section_get_string(section, "path",
+ &xserver, XSERVER_PATH);
+ custom_env_init_from_environ(&child_env);
+ custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
+
+ custom_env_add_arg(&child_env, xserver);
+ custom_env_add_arg(&child_env, display);
+ custom_env_add_arg(&child_env, "-rootless");
+ custom_env_add_arg(&child_env, LISTEN_STR);
+ custom_env_add_arg(&child_env, x11_abstract_socket.str1);
+ custom_env_add_arg(&child_env, LISTEN_STR);
+ custom_env_add_arg(&child_env, x11_unix_socket.str1);
+ custom_env_add_arg(&child_env, "-displayfd");
+ custom_env_add_arg(&child_env, display_pipe.str1);
+ custom_env_add_arg(&child_env, "-wm");
+ custom_env_add_arg(&child_env, x11_wm_socket.str1);
+ custom_env_add_arg(&child_env, "-terminate");
+
+ wxw->process = client_launch(wxw->compositor, &child_env,
+ no_cloexec_fds, num_no_cloexec_fds,
+ xserver_cleanup, wxw);
+ if (!wxw->process) {
+ weston_log("Couldn't start Xwayland\n");
+ goto err;
+ }
+
+ client = wl_client_create(wxw->compositor->wl_display,
+ wayland_socket.fds[0]);
+ if (!client) {
+ weston_log("Couldn't create client for Xwayland\n");
+ goto err_proc;
}
- return pid;
+ wxw->wm_fd = x11_wm_socket.fds[0];
+
+ /* Now we can no longer fail, close the child end of our sockets */
+ close(wayland_socket.fds[1]);
+ close(x11_wm_socket.fds[1]);
+ close(display_pipe.fds[1]);
+
+ /* During initialization the X server will round trip
+ * and block on the wayland compositor, so avoid making
+ * blocking requests (like xcb_connect_to_fd) until
+ * it's done with that. */
+ loop = wl_display_get_event_loop(wxw->compositor->wl_display);
+ wxw->display_fd_source =
+ wl_event_loop_add_fd(loop, display_pipe.fds[0],
+ WL_EVENT_READABLE,
+ handle_display_fd, wxw);
+
+ free(xserver);
+
+ return client;
+
+
+err_proc:
+ wl_list_remove(&wxw->process->link);
+err:
+ free(xserver);
+ fdstr_close_all(&display_pipe);
+ fdstr_close_all(&x11_wm_socket);
+ fdstr_close_all(&wayland_socket);
+ free(wxw->process);
+ return NULL;
}
-static void
-xserver_cleanup(struct weston_process *process, int status)
+void
+wet_xwayland_destroy(struct weston_compositor *compositor, void *data)
{
- struct wet_xwayland *wxw =
- container_of(process, struct wet_xwayland, process);
- struct wl_event_loop *loop =
- wl_display_get_event_loop(wxw->ivi_compositor->compositor->wl_display);
-
- wxw->api->xserver_exited(wxw->xwayland, status);
- wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
- handle_sigusr1, wxw);
- wxw->client = NULL;
+ struct wet_xwayland *wxw = data;
+
+ /* Calling this will call the process cleanup, in turn cleaning up the
+ * client and the core Xwayland state */
+ if (wxw->process)
+ process_destroy(wxw->process, 0, true);
+
+ free(wxw);
}
-int
-wet_load_xwayland(struct weston_compositor *comp)
+void *
+wet_load_xwayland(struct weston_compositor *compositor)
{
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wet_xwayland *wxw;
- struct wl_event_loop *loop;
- struct ivi_compositor *ivi = to_ivi_compositor(comp);
- if (weston_compositor_load_xwayland(comp) < 0)
- return -1;
+ if (weston_compositor_load_xwayland(compositor) < 0)
+ return NULL;
- api = weston_xwayland_get_api(comp);
+ api = weston_xwayland_get_api(compositor);
if (!api) {
weston_log("Failed to get the xwayland module API.\n");
- return -1;
+ return NULL;
}
- xwayland = api->get(comp);
+ xwayland = api->get(compositor);
if (!xwayland) {
weston_log("Failed to get the xwayland object.\n");
- return -1;
+ return NULL;
}
wxw = zalloc(sizeof *wxw);
if (!wxw)
- return -1;
+ return NULL;
- wxw->ivi_compositor = ivi;
+ wxw->compositor = compositor;
wxw->api = api;
wxw->xwayland = xwayland;
- wxw->process.cleanup = xserver_cleanup;
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
- return -1;
+ return NULL;
- loop = wl_display_get_event_loop(comp->wl_display);
- wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
- handle_sigusr1, wxw);
-
- return 0;
+ return wxw;
}