From 18607e2245d1e9ccfdd19db894e4cfdb52def303 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 8 Apr 2024 15:37:42 +0300 Subject: 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 Change-Id: Id421bdd04fc6943e991cbf51e9478450411721ef --- meson.build | 2 + shared/process-util.c | 3 +- shared/process-util.h | 1 + src/desktop.c | 4 +- src/xwayland.c | 408 ++++++++++++++++++++++++++++++++++---------------- 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 #include #include +#include #include #include #include #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; } -- cgit 1.2.3-korg