diff --git a/common/configuration.c b/common/configuration.c index 30580ead..1d9f6297 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 60e3e8b4..991e5e85 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 834f7bb3..509e34a1 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 0dd1783a..0c161d49 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 0ccfcd78..1770180e 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 00000000..bea3f157 --- /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 00000000..5e7da394 --- /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 00000000..ebb18b67 --- /dev/null +++ b/src/wayland-system-compositor.c @@ -0,0 +1,543 @@ +/* + * 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 */ + 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 */ + 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 00000000..5106c3c7 --- /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_ */