summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.h.meson3
-rw-r--r--meson.build4
-rw-r--r--meson_options.txt6
-rw-r--r--src/wayland/meta-xwayland.c149
4 files changed, 161 insertions, 1 deletions
diff --git a/config.h.meson b/config.h.meson
index f4d6000e1..4b7a4a107 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -82,3 +82,6 @@
/* Whether the memfd_create function exists */
#mesondefine HAVE_MEMFD_CREATE
+
+/* List of executable names to ignore when terminating Xwayland */
+#mesondefine XWAYLAND_IGNORE_EXECUTABLES
diff --git a/meson.build b/meson.build
index 5b4be8081..2cf119796 100644
--- a/meson.build
+++ b/meson.build
@@ -461,6 +461,10 @@ xwayland_grab_default_access_rules = get_option('xwayland_grab_default_access_ru
cdata.set_quoted('XWAYLAND_GRAB_DEFAULT_ACCESS_RULES',
xwayland_grab_default_access_rules)
+xwayland_ignore_executables = get_option('xwayland_ignore_executables')
+cdata.set_quoted('XWAYLAND_IGNORE_EXECUTABLES',
+ xwayland_ignore_executables)
+
cdata.set_quoted('MUTTER_PLUGIN_DIR', join_paths(pkglibdir, 'plugins'))
cdata.set_quoted('MUTTER_LOCALEDIR', localedir)
cdata.set_quoted('MUTTER_LIBEXECDIR', libexecdir)
diff --git a/meson_options.txt b/meson_options.txt
index 601f0a773..a0fb61636 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -158,3 +158,9 @@ option('xwayland_initfd',
value: 'auto',
description: 'Whether -initfd argument is passed to Xwayland to guarantee services (e.g. gsd-xsettings) startup before applications'
)
+
+option('xwayland_ignore_executables',
+ type: 'string',
+ value: 'gsd-xsettings,ibus-x11,pulseaudio,Xwayland',
+ description: 'Comma delimited list of executable names to ignore when terminating Xwayland'
+)
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c
index b9832a587..c26a5f539 100644
--- a/src/wayland/meta-xwayland.c
+++ b/src/wayland/meta-xwayland.c
@@ -41,6 +41,9 @@
#include <unistd.h>
#include <X11/extensions/Xrandr.h>
#include <X11/Xauth.h>
+#include <X11/Xlib-xcb.h>
+
+#include <xcb/res.h>
#include "backends/meta-monitor-manager-private.h"
#include "backends/meta-settings-private.h"
@@ -126,6 +129,146 @@ meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface)
return wl_resource_get_client (surface->resource) == manager->client;
}
+static char *
+meta_xwayland_get_exe_from_proc_entry (const char *proc_entry)
+{
+ g_autofree char *exepath;
+ char *executable;
+ char *p;
+
+ exepath = g_file_read_link (proc_entry, NULL);
+ if (!exepath)
+ return NULL;
+
+ p = strrchr (exepath, G_DIR_SEPARATOR);
+ if (p)
+ executable = g_strdup (++p);
+ else
+ executable = g_strdup (exepath);
+
+ return executable;
+}
+
+static char *
+meta_xwayland_get_exe_from_pid (uint32_t pid)
+{
+ g_autofree char *proc_entry;
+ char *executable;
+
+ proc_entry = g_strdup_printf ("/proc/%i/exe", pid);
+ executable = meta_xwayland_get_exe_from_proc_entry (proc_entry);
+
+ return executable;
+}
+
+static char *
+meta_xwayland_get_self_exe (void)
+{
+ g_autofree char *proc_entry;
+ char *executable;
+
+ proc_entry = g_strdup_printf ("/proc/self/exe");
+ executable = meta_xwayland_get_exe_from_proc_entry (proc_entry);
+
+ return executable;
+}
+
+static gboolean
+can_xwayland_ignore_exe (const char *executable,
+ const char *self)
+{
+ char ** ignore_executables;
+ gboolean ret;
+
+ if (!g_strcmp0 (executable, self))
+ return TRUE;
+
+ ignore_executables = g_strsplit_set (XWAYLAND_IGNORE_EXECUTABLES, ",", -1);
+ ret = g_strv_contains ((const char * const *) ignore_executables, executable);
+ g_strfreev (ignore_executables);
+
+ return ret;
+}
+
+static uint32_t
+meta_xwayland_get_client_pid (xcb_connection_t *xcb,
+ uint32_t client)
+{
+ xcb_res_client_id_spec_t spec = { 0 };
+ xcb_res_query_client_ids_cookie_t cookie;
+ xcb_res_query_client_ids_reply_t *reply = NULL;
+ uint32_t pid = 0, *value;
+
+ spec.client = client;
+ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
+
+ cookie = xcb_res_query_client_ids (xcb, 1, &spec);
+ reply = xcb_res_query_client_ids_reply (xcb, cookie, NULL);
+
+ if (reply == NULL)
+ return 0;
+
+ xcb_res_client_id_value_iterator_t it;
+ for (it = xcb_res_query_client_ids_ids_iterator (reply);
+ it.rem;
+ xcb_res_client_id_value_next (&it))
+ {
+ spec = it.data->spec;
+ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID)
+ {
+ value = xcb_res_client_id_value_value (it.data);
+ pid = *value;
+ break;
+ }
+ }
+ free (reply);
+
+ return pid;
+}
+
+static gboolean
+can_terminate_xwayland (Display *xdisplay)
+{
+ xcb_connection_t *xcb = XGetXCBConnection (xdisplay);
+ xcb_res_query_clients_cookie_t cookie;
+ xcb_res_query_clients_reply_t *reply = NULL;
+ xcb_res_client_iterator_t it;
+ gboolean can_terminate;
+ char *self;
+
+ cookie = xcb_res_query_clients (xcb);
+ reply = xcb_res_query_clients_reply (xcb, cookie, NULL);
+
+ /* Could not get the list of X11 clients, better not terminate Xwayland */
+ if (reply == NULL)
+ return FALSE;
+
+ can_terminate = TRUE;
+ self = meta_xwayland_get_self_exe ();
+ for (it = xcb_res_query_clients_clients_iterator (reply);
+ it.rem && can_terminate;
+ xcb_res_client_next (&it))
+ {
+ uint32_t pid;
+ char *executable;
+
+ pid = meta_xwayland_get_client_pid (xcb, it.data->resource_base);
+ if (pid == 0)
+ {
+ /* Unknown PID, don't risk terminating it */
+ can_terminate = FALSE;
+ break;
+ }
+
+ executable = meta_xwayland_get_exe_from_pid (pid);
+ can_terminate = can_xwayland_ignore_exe (executable, self);
+ g_free (executable);
+ }
+ free (reply);
+
+ return can_terminate;
+}
+
static gboolean
try_display (int display,
char **filename_out,
@@ -385,10 +528,14 @@ static gboolean
shutdown_xwayland_cb (gpointer data)
{
MetaXWaylandManager *manager = data;
+ MetaDisplay *display = meta_get_display ();
+
+ if (!can_terminate_xwayland (display->x11_display->xdisplay))
+ return G_SOURCE_CONTINUE;
meta_verbose ("Shutting down Xwayland");
manager->xserver_grace_period_id = 0;
- meta_display_shutdown_x11 (meta_get_display ());
+ meta_display_shutdown_x11 (display);
meta_xwayland_stop_xserver (manager);
return G_SOURCE_REMOVE;
}