From 1c1dff2a4c840d3794490295f5cf5bff90da32d9 Mon Sep 17 00:00:00 2001 From: Marius Gripsgard Date: Tue, 14 Jul 2020 00:03:41 +0200 Subject: [PATCH 1/2] Add a generic wayland system compositor support This adds a generic wayland system compositor impl based on the old unity-system-compositor. This allows lightdm to handle a generic nested wayland compositor setup. This does not implement xwayland to allow to run x sessions on top of a wayland system compositor. --- common/configuration.c | 2 + data/lightdm.conf | 6 +- src/Makefile.am | 4 + src/display-manager.c | 2 + src/lightdm.c | 2 + src/seat-wayland-system-compositor.c | 293 +++++++++++++++ src/seat-wayland-system-compositor.h | 39 ++ src/wayland-system-compositor.c | 538 +++++++++++++++++++++++++++ src/wayland-system-compositor.h | 56 +++ 9 files changed, 941 insertions(+), 1 deletion(-) create mode 100644 src/seat-wayland-system-compositor.c create mode 100644 src/seat-wayland-system-compositor.h create mode 100644 src/wayland-system-compositor.c create mode 100644 src/wayland-system-compositor.h diff --git a/common/configuration.c b/common/configuration.c index 30580ead6..1d9f6297a 100644 --- a/common/configuration.c +++ b/common/configuration.c @@ -366,6 +366,8 @@ config_init (Configuration *config) g_hash_table_insert (config->priv->seat_keys, "xdmcp-key", GINT_TO_POINTER (KEY_SUPPORTED)); g_hash_table_insert (config->priv->seat_keys, "unity-compositor-command", GINT_TO_POINTER (KEY_DEPRECATED)); g_hash_table_insert (config->priv->seat_keys, "unity-compositor-timeout", GINT_TO_POINTER (KEY_DEPRECATED)); + g_hash_table_insert (config->priv->seat_keys, "wayland-compositor-command", GINT_TO_POINTER (KEY_SUPPORTED)); + g_hash_table_insert (config->priv->seat_keys, "wayland-compositor-timeout", GINT_TO_POINTER (KEY_SUPPORTED)); g_hash_table_insert (config->priv->seat_keys, "greeter-session", GINT_TO_POINTER (KEY_SUPPORTED)); g_hash_table_insert (config->priv->seat_keys, "greeter-hide-users", GINT_TO_POINTER (KEY_SUPPORTED)); g_hash_table_insert (config->priv->seat_keys, "greeter-allow-guest", GINT_TO_POINTER (KEY_SUPPORTED)); diff --git a/data/lightdm.conf b/data/lightdm.conf index 60e3e8b40..991e5e853 100644 --- a/data/lightdm.conf +++ b/data/lightdm.conf @@ -44,7 +44,7 @@ # [Seat:seat0] matches the seat named "seat0". # [Seat:seat-thin-client*] matches all seats that have names that start with "seat-thin-client". # -# type = Seat type (local, xremote) +# type = Seat type (local, xremote, wayland-system-compositor) # pam-service = PAM service to use for login # pam-autologin-service = PAM service to use for autologin # pam-greeter-service = PAM service to use for greeters @@ -59,6 +59,8 @@ # xdmcp-manager = XDMCP manager to connect to (implies xserver-allow-tcp=true) # xdmcp-port = XDMCP UDP/IP port to communicate on # xdmcp-key = Authentication key to use for XDM-AUTHENTICATION-1 (stored in keys.conf) +# wayland-compositor-command = Wayland compositor command to run (can also contain arguments e.g. wayland-system-compositor -special-option) +# wayland-compositor-timeout = Number of seconds to wait for compositor to start # greeter-session = Session to load for greeter # greeter-hide-users = True to hide the user list # greeter-allow-guest = True if the greeter should show a guest login option @@ -99,6 +101,8 @@ #xdmcp-manager= #xdmcp-port=177 #xdmcp-key= +#wayland-compositor-command=wayland-system-compositor +#wayland-compositor-timeout=60 #greeter-session=example-gtk-gnome #greeter-hide-users=false #greeter-allow-guest=true diff --git a/src/Makefile.am b/src/Makefile.am index 834f7bb33..509e34a16 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,6 +35,8 @@ lightdm_SOURCES = \ seat.h \ seat-local.c \ seat-local.h \ + seat-wayland-system-compositor.c \ + seat-wayland-system-compositor.h \ seat-xdmcp-session.c \ seat-xdmcp-session.h \ seat-xremote.c \ @@ -49,6 +51,8 @@ lightdm_SOURCES = \ session-config.h \ shared-data-manager.c \ shared-data-manager.h \ + wayland-system-compositor.c \ + wayland-system-compositor.h \ vnc-server.c \ vnc-server.h \ vt.c \ diff --git a/src/display-manager.c b/src/display-manager.c index 0dd1783aa..0c161d497 100644 --- a/src/display-manager.c +++ b/src/display-manager.c @@ -19,6 +19,7 @@ #include "configuration.h" #include "seat-local.h" #include "seat-xremote.h" +#include "seat-wayland-system-compositor.h" #include "plymouth.h" enum { @@ -166,6 +167,7 @@ display_manager_init (DisplayManager *manager) /* Load the seat modules */ seat_register_module ("local", SEAT_LOCAL_TYPE); seat_register_module ("xremote", SEAT_XREMOTE_TYPE); + seat_register_module ("wayland-system-compositor", SEAT_WAYLAND_SYSTEM_COMPOSITOR_TYPE); } static void diff --git a/src/lightdm.c b/src/lightdm.c index 0ccfcd78f..1770180ed 100644 --- a/src/lightdm.c +++ b/src/lightdm.c @@ -787,6 +787,8 @@ main (int argc, char **argv) config_set_string (config_get_instance (), "Seat:*", "xmir-command", "Xmir"); if (!config_has_key (config_get_instance (), "Seat:*", "xserver-share")) config_set_boolean (config_get_instance (), "Seat:*", "xserver-share", TRUE); + if (!config_has_key (config_get_instance (), "Seat:*", "wayland-compositor-command")) + config_set_string (config_get_instance (), "Seat:*", "wayland-compositor-command", "wayland-system-compositor"); if (!config_has_key (config_get_instance (), "Seat:*", "start-session")) config_set_boolean (config_get_instance (), "Seat:*", "start-session", TRUE); if (!config_has_key (config_get_instance (), "Seat:*", "allow-user-switching")) diff --git a/src/seat-wayland-system-compositor.c b/src/seat-wayland-system-compositor.c new file mode 100644 index 000000000..bea3f1577 --- /dev/null +++ b/src/seat-wayland-system-compositor.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2012-2013 Robert Ancell. + * Copyright (C) 2020 UBports Foundation. + * Author(s): Robert Ancell + * Marius Gripsgard + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#include +#include +#include +#include + +#include "seat-wayland-system-compositor.h" +#include "configuration.h" +#include "wayland-system-compositor.h" +#include "vt.h" +#include "plymouth.h" + +typedef struct +{ + /* System compositor */ + WaylandSystemCompositor *compositor; + + /* The currently visible session */ + Session *active_session; + DisplayServer *active_display_server; +} SeatWaylandSystemCompositorPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SeatWaylandSystemCompositor, seat_wayland_system_compositor, SEAT_TYPE) + +static void +seat_wayland_system_compositor_setup (Seat *seat) +{ + seat_set_supports_multi_session (seat, TRUE); + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->setup (seat); +} + +static void +check_stopped (SeatWaylandSystemCompositor *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (seat); + if (!priv->compositor) + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->stop (SEAT (seat)); +} + +static void +compositor_ready_cb (WaylandSystemCompositor *compositor, SeatWaylandSystemCompositor *seat) +{ + l_debug (seat, "Compositor ready"); + + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->start (SEAT (seat)); +} + +static void +compositor_stopped_cb (WaylandSystemCompositor *compositor, SeatWaylandSystemCompositor *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (seat); + + l_debug (seat, "Compositor stopped"); + + g_clear_object (&priv->compositor); + + if (seat_get_is_stopping (SEAT (seat))) + check_stopped (seat); + else + seat_stop (SEAT (seat)); +} + +static gboolean +seat_wayland_system_compositor_start (Seat *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + /* Replace Plymouth if it is running */ + gint vt = -1; + if (plymouth_get_is_active () && plymouth_has_active_vt ()) + { + gint active_vt = vt_get_active (); + if (active_vt >= vt_get_min ()) + { + vt = active_vt; + plymouth_quit (TRUE); + } + else + l_debug (seat, "Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ()); + } + if (plymouth_get_is_active ()) + plymouth_quit (FALSE); + if (vt < 0) + vt = vt_can_multi_seat () ? vt_get_unused () : 0; + if (vt < 0) + { + l_debug (seat, "Failed to get a VT to run on"); + return FALSE; + } + + int timeout = seat_get_integer_property (SEAT (seat), "wayland-compositor-timeout"); + if (timeout <= 0) + timeout = 60; + + priv->compositor = wayland_system_compositor_new (); + g_signal_connect (priv->compositor, DISPLAY_SERVER_SIGNAL_READY, G_CALLBACK (compositor_ready_cb), seat); + g_signal_connect (priv->compositor, DISPLAY_SERVER_SIGNAL_STOPPED, G_CALLBACK (compositor_stopped_cb), seat); + wayland_system_compositor_set_command (priv->compositor, seat_get_string_property (SEAT (seat), "wayland-compositor-command")); + wayland_system_compositor_set_vt (priv->compositor, vt); + wayland_system_compositor_set_timeout (priv->compositor, timeout); + + return display_server_start (DISPLAY_SERVER (priv->compositor)); +} + +static DisplayServer * +seat_wayland_system_compositor_create_display_server (Seat *seat, Session *session) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + const gchar *session_type = session_get_session_type (session); + if (strcmp (session_type, "mir") == 0 || strcmp (session_type, "wayland") == 0) + return g_object_ref (DISPLAY_SERVER (priv->compositor)); + else + { + l_warning (seat, "Can't create unsupported display server '%s'", session_type); + return NULL; + } +} + +static gboolean +seat_wayland_system_compositor_display_server_is_used (Seat *seat, DisplayServer *display_server) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + if (display_server == DISPLAY_SERVER (priv->compositor)) + return TRUE; + + return SEAT_CLASS (seat_wayland_system_compositor_parent_class)->display_server_is_used (seat, display_server); +} + +static GreeterSession * +seat_wayland_system_compositor_create_greeter_session (Seat *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + GreeterSession *greeter_session = SEAT_CLASS (seat_wayland_system_compositor_parent_class)->create_greeter_session (seat); + session_set_env (SESSION (greeter_session), "XDG_SEAT", seat_get_name (seat)); + + gint vt = display_server_get_vt (DISPLAY_SERVER (priv->compositor)); + if (vt >= 0) + { + g_autofree gchar *value = g_strdup_printf ("%d", vt); + session_set_env (SESSION (greeter_session), "XDG_VTNR", value); + } + + return greeter_session; +} + +static Session * +seat_wayland_system_compositor_create_session (Seat *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + Session *session = SEAT_CLASS (seat_wayland_system_compositor_parent_class)->create_session (seat); + session_set_env (session, "XDG_SEAT", seat_get_name (seat)); + + gint vt = display_server_get_vt (DISPLAY_SERVER (priv->compositor)); + if (vt >= 0) + { + g_autofree gchar *value = g_strdup_printf ("%d", vt); + session_set_env (SESSION (session), "XDG_VTNR", value); + } + + return session; +} + +static const gchar * +get_mir_id (Session *session) +{ + if (!session) + return NULL; + + DisplayServer *display_server = session_get_display_server (session); + if (IS_WAYLAND_SYSTEM_COMPOSITOR (display_server)) + return session_get_env (session, "MIR_SERVER_NAME"); + + return NULL; +} + +static void +seat_wayland_system_compositor_set_active_session (Seat *seat, Session *session) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + const gchar *old_id = get_mir_id (priv->active_session); + const gchar *new_id = get_mir_id (session); + + g_clear_object (&priv->active_session); + priv->active_session = g_object_ref (session); + + if (g_strcmp0 (old_id, new_id) != 0) + wayland_system_compositor_set_active_session (priv->compositor, new_id); + + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->set_active_session (seat, session); +} + +static Session * +seat_wayland_system_compositor_get_active_session (Seat *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + return priv->active_session; +} + +static void +seat_wayland_system_compositor_set_next_session (Seat *seat, Session *session) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + if (!session) + return; + + const gchar *id = session_get_env (session, "MIR_SERVER_NAME"); + + if (id) + { + l_debug (seat, "Marking Mir session %s as the next session", id); + wayland_system_compositor_set_next_session (priv->compositor, id); + } + else + { + l_debug (seat, "Failed to work out session ID to mark"); + } + + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->set_next_session (seat, session); +} + +static void +seat_wayland_system_compositor_run_script (Seat *seat, DisplayServer *display_server, Process *script) +{ + SEAT_CLASS (seat_wayland_system_compositor_parent_class)->run_script (seat, display_server, script); +} + +static void +seat_wayland_system_compositor_stop (Seat *seat) +{ + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); + + /* Stop the compositor first */ + if (priv->compositor) + display_server_stop (DISPLAY_SERVER (priv->compositor)); + + check_stopped (SEAT_WAYLAND_SYSTEM_COMPOSITOR (seat)); +} + +static void +seat_wayland_system_compositor_init (SeatWaylandSystemCompositor *seat) +{ +} + +static void +seat_wayland_system_compositor_finalize (GObject *object) +{ + SeatWaylandSystemCompositor *seat = SEAT_WAYLAND_SYSTEM_COMPOSITOR (object); + SeatWaylandSystemCompositorPrivate *priv = seat_wayland_system_compositor_get_instance_private (seat); + + g_clear_object (&priv->compositor); + g_clear_object (&priv->active_session); + g_clear_object (&priv->active_display_server); + + G_OBJECT_CLASS (seat_wayland_system_compositor_parent_class)->finalize (object); +} + +static void +seat_wayland_system_compositor_class_init (SeatWaylandSystemCompositorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SeatClass *seat_class = SEAT_CLASS (klass); + + object_class->finalize = seat_wayland_system_compositor_finalize; + seat_class->setup = seat_wayland_system_compositor_setup; + seat_class->start = seat_wayland_system_compositor_start; + seat_class->create_display_server = seat_wayland_system_compositor_create_display_server; + seat_class->display_server_is_used = seat_wayland_system_compositor_display_server_is_used; + seat_class->create_greeter_session = seat_wayland_system_compositor_create_greeter_session; + seat_class->create_session = seat_wayland_system_compositor_create_session; + seat_class->set_active_session = seat_wayland_system_compositor_set_active_session; + seat_class->get_active_session = seat_wayland_system_compositor_get_active_session; + seat_class->set_next_session = seat_wayland_system_compositor_set_next_session; + seat_class->run_script = seat_wayland_system_compositor_run_script; + seat_class->stop = seat_wayland_system_compositor_stop; +} diff --git a/src/seat-wayland-system-compositor.h b/src/seat-wayland-system-compositor.h new file mode 100644 index 000000000..5e7da394f --- /dev/null +++ b/src/seat-wayland-system-compositor.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012-2013 Robert Ancell. + * Copyright (C) 2020 UBports Foundation. + * Author(s): Robert Ancell + * Marius Gripsgard + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#ifndef _SEAT_WAYLAND_SYSTEM_COMPOSITOR_H_ +#define _SEAT_WAYLAND_SYSTEM_COMPOSITOR_H_ + +#include +#include "seat.h" + +G_BEGIN_DECLS + +#define SEAT_WAYLAND_SYSTEM_COMPOSITOR_TYPE (seat_wayland_system_compositor_get_type()) +#define SEAT_WAYLAND_SYSTEM_COMPOSITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAT_WAYLAND_SYSTEM_COMPOSITOR_TYPE, SeatWaylandSystemCompositor)) + +typedef struct +{ + Seat parent_instance; +} SeatWaylandSystemCompositor; + +typedef struct +{ + SeatClass parent_class; +} SeatWaylandSystemCompositorClass; + +GType seat_wayland_system_compositor_get_type (void); + +G_END_DECLS + +#endif /* _SEAT_WAYLAND_SYSTEM_COMPOSITOR_H_ */ diff --git a/src/wayland-system-compositor.c b/src/wayland-system-compositor.c new file mode 100644 index 000000000..6e175fb59 --- /dev/null +++ b/src/wayland-system-compositor.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2013 Canonical Ltd. + * Copyright (C) 2020 UBports Foundation. + * Author(s): Robert Ancell + * Marius Gripsgard + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wayland-system-compositor.h" +#include "configuration.h" +#include "process.h" +#include "greeter-session.h" +#include "vt.h" + +typedef struct +{ + /* Compositor process */ + Process *process; + + /* Command to run the compositor */ + gchar *command; + + /* Socket to communicate on */ + gchar *socket; + + /* VT to run on */ + gint vt; + gboolean have_vt_ref; + + /* Pipes to communicate with compositor */ + int to_compositor_pipe[2]; + int from_compositor_pipe[2]; + + /* IO channel listening on for messages from the compositor */ + GIOChannel *from_compositor_channel; + guint from_compositor_watch; + + /* Buffer reading from channel */ + guint8 *read_buffer; + gsize read_buffer_length; + gsize read_buffer_n_used; + + /* Timeout when waiting for compositor to start */ + gint timeout; + guint timeout_source; + + /* TRUE when received ready signal */ + gboolean is_ready; + + /* Counters for IDs to use */ + int next_session_id; + int next_greeter_id; +} WaylandSystemCompositorPrivate; + +static void wayland_system_compositor_logger_iface_init (LoggerInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (WaylandSystemCompositor, wayland_system_compositor, DISPLAY_SERVER_TYPE, + G_ADD_PRIVATE (WaylandSystemCompositor) + G_IMPLEMENT_INTERFACE (LOGGER_TYPE, wayland_system_compositor_logger_iface_init)) + +typedef enum +{ + USC_MESSAGE_PING = 0, + USC_MESSAGE_PONG = 1, + USC_MESSAGE_READY = 2, + USC_MESSAGE_SESSION_CONNECTED = 3, + USC_MESSAGE_SET_ACTIVE_SESSION = 4, + USC_MESSAGE_SET_NEXT_SESSION = 5, +} USCMessageID; + +WaylandSystemCompositor * +wayland_system_compositor_new (void) +{ + return g_object_new (WAYLAND_SYSTEM_COMPOSITOR_TYPE, NULL); +} + +void +wayland_system_compositor_set_command (WaylandSystemCompositor *compositor, const gchar *command) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + g_return_if_fail (compositor != NULL); + g_return_if_fail (command != NULL); + + g_free (priv->command); + priv->command = g_strdup (command); +} + +void +wayland_system_compositor_set_socket (WaylandSystemCompositor *compositor, const gchar *socket) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + g_return_if_fail (compositor != NULL); + g_free (priv->socket); + priv->socket = g_strdup (socket); +} + +const gchar * +wayland_system_compositor_get_socket (WaylandSystemCompositor *compositor) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + g_return_val_if_fail (compositor != NULL, NULL); + return priv->socket; +} + +void +wayland_system_compositor_set_vt (WaylandSystemCompositor *compositor, gint vt) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + g_return_if_fail (compositor != NULL); + + if (priv->have_vt_ref) + vt_unref (priv->vt); + priv->have_vt_ref = FALSE; + priv->vt = vt; + if (vt > 0) + { + vt_ref (vt); + priv->have_vt_ref = TRUE; + } +} + +void +wayland_system_compositor_set_timeout (WaylandSystemCompositor *compositor, gint timeout) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + g_return_if_fail (compositor != NULL); + priv->timeout = timeout; +} + +static void +write_message (WaylandSystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + gsize data_length = 4 + payload_length; + g_autofree guint8 *data = g_malloc (data_length); + data[0] = id >> 8; + data[1] = id & 0xFF; + data[2] = payload_length >> 8; + data[3] = payload_length & 0xFF; + if (payload) + memcpy (data + 4, payload, payload_length); + + errno = 0; + if (write (priv->to_compositor_pipe[1], data, data_length) != data_length) + l_warning (compositor, "Failed to write to compositor: %s", strerror (errno)); +} + +void +wayland_system_compositor_set_active_session (WaylandSystemCompositor *compositor, const gchar *id) +{ + g_return_if_fail (compositor != NULL); + write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id)); +} + +void +wayland_system_compositor_set_next_session (WaylandSystemCompositor *compositor, const gchar *id) +{ + g_return_if_fail (compositor != NULL); + write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id)); +} + +static gint +wayland_system_compositor_get_vt (DisplayServer *server) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (WAYLAND_SYSTEM_COMPOSITOR (server)); + g_return_val_if_fail (server != NULL, 0); + return priv->vt; +} + +static void +wayland_system_compositor_connect_session (DisplayServer *display_server, Session *session) +{ + WaylandSystemCompositor *compositor = WAYLAND_SYSTEM_COMPOSITOR (display_server); + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + session_set_env (session, "XDG_SESSION_TYPE", "wayland"); + + if (priv->socket) + session_set_env (session, "WAYLAND_DISPLAY", priv->socket); + + if (!session_get_env (session, "MIR_SERVER_NAME")) + { + g_autofree gchar *name = NULL; + if (IS_GREETER_SESSION (session)) + { + name = g_strdup_printf ("greeter-%d", priv->next_greeter_id); + priv->next_greeter_id++; + } + else + { + name = g_strdup_printf ("session-%d", priv->next_session_id); + priv->next_session_id++; + } + session_set_env (session, "MIR_SERVER_NAME", name); + } + + if (priv->vt >= 0) + { + g_autofree gchar *value = g_strdup_printf ("%d", priv->vt); + session_set_env (session, "XDG_VTNR", value); + } +} + +static void +wayland_system_compositor_disconnect_session (DisplayServer *display_server, Session *session) +{ + session_unset_env (session, "XDG_SESSION_TYPE"); + session_unset_env (session, "WAYLAND_DISPLAY"); + session_unset_env (session, "MIR_SERVER_NAME"); + session_unset_env (session, "XDG_VTNR"); +} + +static gchar * +get_absolute_command (const gchar *command) +{ + g_auto(GStrv) tokens = g_strsplit (command, " ", 2); + + g_autofree gchar *absolute_binary = g_find_program_in_path (tokens[0]); + gchar *absolute_command = NULL; + if (absolute_binary) + { + if (tokens[1]) + absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL); + else + absolute_command = g_strdup (absolute_binary); + } + + return absolute_command; +} + +static gboolean +read_cb (GIOChannel *source, GIOCondition condition, gpointer data) +{ + WaylandSystemCompositor *compositor = data; + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + if (condition == G_IO_HUP) + { + l_debug (compositor, "Compositor closed communication channel"); + priv->from_compositor_watch = 0; + return FALSE; + } + + /* Work out how much required for a message */ + gsize n_to_read = 0; + if (priv->read_buffer_n_used < 4) + n_to_read = 4 - priv->read_buffer_n_used; + else + { + guint16 payload_length = priv->read_buffer[2] << 8 | priv->read_buffer[3]; + n_to_read = 4 + payload_length - priv->read_buffer_n_used; + } + + /* Read from compositor */ + if (n_to_read > 0) + { + gsize n_total = priv->read_buffer_n_used + n_to_read; + if (priv->read_buffer_length < n_total) + priv->read_buffer = g_realloc (priv->read_buffer, n_total); + + g_autoptr(GError) error = NULL; + gsize n_read = 0; + GIOStatus status = g_io_channel_read_chars (source, + (gchar *)priv->read_buffer + priv->read_buffer_n_used, + n_to_read, + &n_read, + &error); + if (error) + l_warning (compositor, "Failed to read from compositor: %s", error->message); + if (status != G_IO_STATUS_NORMAL) + return TRUE; + priv->read_buffer_n_used += n_read; + } + + /* Read header */ + if (priv->read_buffer_n_used < 4) + return TRUE; + guint16 id = priv->read_buffer[0] << 8 | priv->read_buffer[1]; + guint16 payload_length = priv->read_buffer[2] << 8 | priv->read_buffer[3]; + + /* Read payload */ + if (priv->read_buffer_n_used < 4 + payload_length) + return TRUE; + /*guint8 *payload = priv->read_buffer + 4;*/ + + switch (id) + { + case USC_MESSAGE_PING: + l_debug (compositor, "PING!"); + write_message (compositor, USC_MESSAGE_PONG, NULL, 0); + break; + case USC_MESSAGE_PONG: + l_debug (compositor, "PONG!"); + break; + case USC_MESSAGE_READY: + l_debug (compositor, "READY"); + if (!priv->is_ready) + { + priv->is_ready = TRUE; + l_debug (compositor, "Compositor ready"); + g_source_remove (priv->timeout_source); + priv->timeout_source = 0; + DISPLAY_SERVER_CLASS (wayland_system_compositor_parent_class)->start (DISPLAY_SERVER (compositor)); + } + break; + case USC_MESSAGE_SESSION_CONNECTED: + l_debug (compositor, "SESSION CONNECTED"); + break; + default: + l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length); + break; + } + + /* Clear buffer */ + priv->read_buffer_n_used = 0; + + return TRUE; +} + +static void +run_cb (Process *process, gpointer user_data) +{ + /* Make input non-blocking */ + int fd = open ("/dev/null", O_RDONLY); + dup2 (fd, STDIN_FILENO); + close (fd); +} + +static gboolean +timeout_cb (gpointer data) +{ + WaylandSystemCompositor *compositor = data; + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + /* Stop the compositor - it is not working */ + display_server_stop (DISPLAY_SERVER (compositor)); + + priv->timeout_source = 0; + + return TRUE; +} + +static void +stopped_cb (Process *process, WaylandSystemCompositor *compositor) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + l_debug (compositor, "Wayland system compositor stopped"); + + if (priv->timeout_source != 0) + g_source_remove (priv->timeout_source); + priv->timeout_source = 0; + + /* Release VT and display number for re-use */ + if (priv->have_vt_ref) + { + vt_unref (priv->vt); + priv->have_vt_ref = FALSE; + } + + DISPLAY_SERVER_CLASS (wayland_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor)); +} + +static gboolean +wayland_system_compositor_start (DisplayServer *server) +{ + WaylandSystemCompositor *compositor = WAYLAND_SYSTEM_COMPOSITOR (server); + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + + g_return_val_if_fail (priv->process == NULL, FALSE); + + priv->is_ready = FALSE; + + g_return_val_if_fail (priv->command != NULL, FALSE); + + /* Create pipes to talk to compositor */ + if (pipe (priv->to_compositor_pipe) < 0 || pipe (priv->from_compositor_pipe) < 0) + { + l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno)); + return FALSE; + } + + /* Don't allow the daemon end of the pipes to be accessed in the compositor */ + fcntl (priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC); + fcntl (priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC); + + /* Listen for messages from the compositor */ + priv->from_compositor_channel = g_io_channel_unix_new (priv->from_compositor_pipe[0]); + priv->from_compositor_watch = g_io_add_watch (priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, compositor); + + /* Setup logging */ + g_autofree gchar *dir = config_get_string (config_get_instance (), "LightDM", "log-directory"); + g_autofree gchar *log_file = g_build_filename (dir, "wayland-system-compositor.log", NULL); + l_debug (compositor, "Logging to %s", log_file); + + /* Setup environment */ + priv->process = process_new (run_cb, compositor); + gboolean backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs"); + process_set_log_file (priv->process, log_file, TRUE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND); + process_set_clear_environment (priv->process, TRUE); + process_set_env (priv->process, "XDG_SEAT", "seat0"); + g_autofree gchar *value = g_strdup_printf ("%d", priv->vt); + process_set_env (priv->process, "XDG_VTNR", value); + /* Variable required for regression tests */ + if (g_getenv ("LIGHTDM_TEST_ROOT")) + { + process_set_env (priv->process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT")); + process_set_env (priv->process, "LD_PRELOAD", g_getenv ("LD_PRELOAD")); + process_set_env (priv->process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH")); + } + + /* Generate command line to run */ + g_autofree gchar *absolute_command = get_absolute_command (priv->command); + if (!absolute_command) + { + l_debug (compositor, "Can't launch compositor %s, not found in path", priv->command); + return FALSE; + } + g_autoptr(GString) command = g_string_new (absolute_command); + g_string_append_printf (command, " --file '%s'", priv->socket); + g_string_append_printf (command, " --from-dm-fd %d --to-dm-fd %d", priv->to_compositor_pipe[0], priv->from_compositor_pipe[1]); + if (priv->vt > 0) + g_string_append_printf (command, " --vt %d", priv->vt); + process_set_command (priv->process, command->str); + + /* Start the compositor */ + g_signal_connect (priv->process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), compositor); + gboolean result = process_start (priv->process, FALSE); + + /* Close compositor ends of the pipes */ + close (priv->to_compositor_pipe[0]); + priv->to_compositor_pipe[0] = -1; + close (priv->from_compositor_pipe[1]); + priv->from_compositor_pipe[1] = -1; + + if (!result) + return FALSE; + + /* Connect to the compositor */ + if (priv->timeout > 0) + { + l_debug (compositor, "Waiting for system compositor for %ds", priv->timeout); + priv->timeout_source = g_timeout_add (priv->timeout * 1000, timeout_cb, compositor); + } + + return TRUE; +} + +static void +wayland_system_compositor_stop (DisplayServer *server) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (WAYLAND_SYSTEM_COMPOSITOR (server)); + process_stop (priv->process); +} + +static void +wayland_system_compositor_init (WaylandSystemCompositor *compositor) +{ + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (compositor); + priv->vt = -1; + priv->command = g_strdup ("wayland-system-compositor"); + priv->socket = g_strdup ("/run/wayland-root"); + priv->timeout = -1; + priv->to_compositor_pipe[0] = -1; + priv->to_compositor_pipe[1] = -1; + priv->from_compositor_pipe[0] = -1; + priv->from_compositor_pipe[1] = -1; +} + +static void +wayland_system_compositor_finalize (GObject *object) +{ + WaylandSystemCompositor *self = WAYLAND_SYSTEM_COMPOSITOR (object); + WaylandSystemCompositorPrivate *priv = wayland_system_compositor_get_instance_private (self); + + if (priv->process) + g_signal_handlers_disconnect_matched (priv->process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); + g_clear_object (&priv->process); + g_clear_pointer (&priv->command, g_free); + g_clear_pointer (&priv->socket, g_free); + if (priv->have_vt_ref) + vt_unref (priv->vt); + close (priv->to_compositor_pipe[0]); + close (priv->to_compositor_pipe[1]); + close (priv->from_compositor_pipe[0]); + close (priv->from_compositor_pipe[1]); + g_clear_pointer (&priv->from_compositor_channel, g_io_channel_unref); + if (priv->from_compositor_watch) + g_source_remove (priv->from_compositor_watch); + g_clear_pointer (&priv->read_buffer, g_free); + if (priv->timeout_source) + g_source_remove (priv->timeout_source); + + G_OBJECT_CLASS (wayland_system_compositor_parent_class)->finalize (object); +} + +static void +wayland_system_compositor_class_init (WaylandSystemCompositorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass); + + display_server_class->get_vt = wayland_system_compositor_get_vt; + display_server_class->connect_session = wayland_system_compositor_connect_session; + display_server_class->disconnect_session = wayland_system_compositor_disconnect_session; + display_server_class->start = wayland_system_compositor_start; + display_server_class->stop = wayland_system_compositor_stop; + object_class->finalize = wayland_system_compositor_finalize; +} + +static gint +wayland_system_compositor_real_logprefix (Logger *self, gchar *buf, gulong buflen) +{ + return g_snprintf (buf, buflen, "Wayland System Compositor: "); +} + +static void +wayland_system_compositor_logger_iface_init (LoggerInterface *iface) +{ + iface->logprefix = &wayland_system_compositor_real_logprefix; +} diff --git a/src/wayland-system-compositor.h b/src/wayland-system-compositor.h new file mode 100644 index 000000000..5106c3c7f --- /dev/null +++ b/src/wayland-system-compositor.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Canonical Ltd. + * Copyright (C) 2020 UBports Foundation. + * Author(s): Robert Ancell + * Marius Gripsgard + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#ifndef WAYLAND_SYSTEM_COMPOSITOR_H_ +#define WAYLAND_SYSTEM_COMPOSITOR_H_ + +#include +#include "display-server.h" + +G_BEGIN_DECLS + +#define WAYLAND_SYSTEM_COMPOSITOR_TYPE (wayland_system_compositor_get_type()) +#define WAYLAND_SYSTEM_COMPOSITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WAYLAND_SYSTEM_COMPOSITOR_TYPE, WaylandSystemCompositor)) +#define IS_WAYLAND_SYSTEM_COMPOSITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WAYLAND_SYSTEM_COMPOSITOR_TYPE)) + +typedef struct +{ + DisplayServer parent_instance; +} WaylandSystemCompositor; + +typedef struct +{ + DisplayServerClass parent_class; +} WaylandSystemCompositorClass; + +GType wayland_system_compositor_get_type (void); + +WaylandSystemCompositor *wayland_system_compositor_new (void); + +void wayland_system_compositor_set_command (WaylandSystemCompositor *compositor, const gchar *command); + +void wayland_system_compositor_set_socket (WaylandSystemCompositor *compositor, const gchar *socket); + +const gchar *wayland_system_compositor_get_socket (WaylandSystemCompositor *compositor); + +void wayland_system_compositor_set_vt (WaylandSystemCompositor *compositor, gint vt); + +void wayland_system_compositor_set_timeout (WaylandSystemCompositor *compositor, gint timeout); + +void wayland_system_compositor_set_active_session (WaylandSystemCompositor *compositor, const gchar *id); + +void wayland_system_compositor_set_next_session (WaylandSystemCompositor *compositor, const gchar *id); + +G_END_DECLS + +#endif /* WAYLAND_SYSTEM_COMPOSITOR_H_ */ From 0c4a3b01849fb5b2e30af97ad05aac7ae3354a1b Mon Sep 17 00:00:00 2001 From: Marius Gripsgard Date: Wed, 24 Mar 2021 22:10:46 +0100 Subject: [PATCH 2/2] WSC: set the GIO encoding for the from USC pipe to binary We always read raw data from the pipe, thus this should have been set since this file was written. However, for some reason this has nerver been set, and for some unknown reason this has never been a problem on Xenial port of UT. However, not having this caused an infinite hang on g_io_channel_read_chars(), presumebly because it's trying to interpret a binary data as an incomplete UTF-8 character. From: https://gitlab.com/ubports/core/packaging/lightdm/-/commit/6616c8a8b6451751d130696f8edcf8aff0c11c35 Author: Ratchanan Srirattanamet --- src/wayland-system-compositor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wayland-system-compositor.c b/src/wayland-system-compositor.c index 6e175fb59..ebb18b67a 100644 --- a/src/wayland-system-compositor.c +++ b/src/wayland-system-compositor.c @@ -402,7 +402,12 @@ wayland_system_compositor_start (DisplayServer *server) fcntl (priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC); /* Listen for messages from the compositor */ + g_autoptr(GError) from_error = NULL; priv->from_compositor_channel = g_io_channel_unix_new (priv->from_compositor_pipe[0]); + g_io_channel_set_encoding(priv->from_compositor_channel, /* encoding */ NULL, &from_error); + if (from_error) + g_warning ("Failed to set encoding on from compositor channel to binary: %s\n", from_error->message); + priv->from_compositor_watch = g_io_add_watch (priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, compositor); /* Setup logging */