Blame SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch

c90517
From 67c08ab2df5f86626b6e13f3318e41187bde0737 Mon Sep 17 00:00:00 2001
c90517
From: Ray Strode <rstrode@redhat.com>
c90517
Date: Thu, 2 Aug 2018 15:06:09 -0400
c90517
Subject: [PATCH 14/48] daemon: kill and restart greeter on demand under
c90517
 wayland
c90517
c90517
Right now we leave the greeter alive after the user logs in.
c90517
This is for two reasons:
c90517
c90517
1) When the greeter is running Xorg, there's no way to kill
c90517
it when it's running on an inactive VT (X jumps to the foreground
c90517
when being killed)
c90517
c90517
2) The greeter, in a way, provides a securepath for unlock.
c90517
Users in theory could know that by hitting ctrl-alt-f1 to secure
c90517
attention, the login screen presented is not spoofed.
c90517
c90517
Since we use wayland by default, 1 isn't that much of a concern,
c90517
and 2 is a bit of niche feature that most users probably haven't
c90517
considered.
c90517
c90517
And there's a huge downside to keeping the greeter alive: it uses
c90517
a very large amount of memory.
c90517
c90517
This commit changes GDM to kill the login screen when switching
c90517
away from the login screen's VT and restarting it when switching
c90517
back.
c90517
c90517
Based heavily on work by Hans de Goede <hdegoede@redhat.com>
c90517
c90517
Closes: https://gitlab.gnome.org/GNOME/gdm/issues/222
c90517
---
c90517
 daemon/gdm-local-display-factory.c | 167 +++++++++++++++++++++++++++++
c90517
 1 file changed, 167 insertions(+)
c90517
c90517
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
c90517
index 7f7735ca1..4ae656ab3 100644
c90517
--- a/daemon/gdm-local-display-factory.c
c90517
+++ b/daemon/gdm-local-display-factory.c
c90517
@@ -34,60 +34,65 @@
c90517
 #include "gdm-manager.h"
c90517
 #include "gdm-display-factory.h"
c90517
 #include "gdm-local-display-factory.h"
c90517
 #include "gdm-local-display-factory-glue.h"
c90517
 
c90517
 #include "gdm-settings-keys.h"
c90517
 #include "gdm-settings-direct.h"
c90517
 #include "gdm-display-store.h"
c90517
 #include "gdm-local-display.h"
c90517
 #include "gdm-legacy-display.h"
c90517
 
c90517
 #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate))
c90517
 
c90517
 #define GDM_DBUS_PATH                       "/org/gnome/DisplayManager"
c90517
 #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory"
c90517
 #define GDM_MANAGER_DBUS_NAME               "org.gnome.DisplayManager.LocalDisplayFactory"
c90517
 
c90517
 #define MAX_DISPLAY_FAILURES 5
c90517
 
c90517
 struct GdmLocalDisplayFactoryPrivate
c90517
 {
c90517
         GdmDBusLocalDisplayFactory *skeleton;
c90517
         GDBusConnection *connection;
c90517
         GHashTable      *used_display_numbers;
c90517
 
c90517
         /* FIXME: this needs to be per seat? */
c90517
         guint            num_failures;
c90517
 
c90517
         guint            seat_new_id;
c90517
         guint            seat_removed_id;
c90517
+
c90517
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
c90517
+        char            *tty_of_active_vt;
c90517
+        guint            active_vt_watch_id;
c90517
+#endif
c90517
 };
c90517
 
c90517
 enum {
c90517
         PROP_0,
c90517
 };
c90517
 
c90517
 static void     gdm_local_display_factory_class_init    (GdmLocalDisplayFactoryClass *klass);
c90517
 static void     gdm_local_display_factory_init          (GdmLocalDisplayFactory      *factory);
c90517
 static void     gdm_local_display_factory_finalize      (GObject                     *object);
c90517
 
c90517
 static GdmDisplay *create_display                       (GdmLocalDisplayFactory      *factory,
c90517
                                                          const char                  *seat_id,
c90517
                                                          const char                  *session_type,
c90517
                                                          gboolean                    initial_display);
c90517
 
c90517
 static void     on_display_status_changed               (GdmDisplay                  *display,
c90517
                                                          GParamSpec                  *arg1,
c90517
                                                          GdmLocalDisplayFactory      *factory);
c90517
 
c90517
 static gboolean gdm_local_display_factory_sync_seats    (GdmLocalDisplayFactory *factory);
c90517
 static gpointer local_display_factory_object = NULL;
c90517
 
c90517
 G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY)
c90517
 
c90517
 GQuark
c90517
 gdm_local_display_factory_error_quark (void)
c90517
 {
c90517
         static GQuark ret = 0;
c90517
         if (ret == 0) {
c90517
                 ret = g_quark_from_static_string ("gdm_local_display_factory_error");
c90517
@@ -507,98 +512,260 @@ gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory)
c90517
 static void
c90517
 on_seat_new (GDBusConnection *connection,
c90517
              const gchar     *sender_name,
c90517
              const gchar     *object_path,
c90517
              const gchar     *interface_name,
c90517
              const gchar     *signal_name,
c90517
              GVariant        *parameters,
c90517
              gpointer         user_data)
c90517
 {
c90517
         const char *seat;
c90517
 
c90517
         g_variant_get (parameters, "(&s&o)", &seat, NULL);
c90517
         create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, NULL, FALSE);
c90517
 }
c90517
 
c90517
 static void
c90517
 on_seat_removed (GDBusConnection *connection,
c90517
                  const gchar     *sender_name,
c90517
                  const gchar     *object_path,
c90517
                  const gchar     *interface_name,
c90517
                  const gchar     *signal_name,
c90517
                  GVariant        *parameters,
c90517
                  gpointer         user_data)
c90517
 {
c90517
         const char *seat;
c90517
 
c90517
         g_variant_get (parameters, "(&s&o)", &seat, NULL);
c90517
         delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
c90517
 }
c90517
 
c90517
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
c90517
+static gboolean
c90517
+lookup_by_session_id (const char *id,
c90517
+                      GdmDisplay *display,
c90517
+                      gpointer    user_data)
c90517
+{
c90517
+        const char *looking_for = user_data;
c90517
+        const char *current;
c90517
+
c90517
+        current = gdm_display_get_session_id (display);
c90517
+        return g_strcmp0 (current, looking_for) == 0;
c90517
+}
c90517
+
c90517
+static void
c90517
+maybe_stop_greeter_display (GdmDisplay *display)
c90517
+{
c90517
+        g_autofree char *display_session_type = NULL;
c90517
+
c90517
+        if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED)
c90517
+                return;
c90517
+
c90517
+        g_object_get (G_OBJECT (display),
c90517
+                      "session-type", &display_session_type,
c90517
+                      NULL);
c90517
+
c90517
+        /* we can only stop greeter for wayland sessions, since
c90517
+         * X server would jump back on exit */
c90517
+        if (g_strcmp0 (display_session_type, "wayland") != 0)
c90517
+                return;
c90517
+
c90517
+        gdm_display_stop_greeter_session (display);
c90517
+        gdm_display_unmanage (display);
c90517
+        gdm_display_finish (display);
c90517
+}
c90517
+
c90517
+static gboolean
c90517
+on_vt_changed (GIOChannel    *source,
c90517
+               GIOCondition   condition,
c90517
+               GdmLocalDisplayFactory *factory)
c90517
+{
c90517
+        GIOStatus status;
c90517
+        static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT;
c90517
+        g_autofree char *tty_of_previous_vt = NULL;
c90517
+        g_autofree char *tty_of_active_vt = NULL;
c90517
+        g_autofree char *login_session_id = NULL;
c90517
+        g_autofree char *active_session_id = NULL;
c90517
+        const char *session_type = NULL;
c90517
+        int ret;
c90517
+
c90517
+        g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL);
c90517
+
c90517
+        if (condition & G_IO_PRI) {
c90517
+                g_autoptr (GError) error = NULL;
c90517
+                status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error);
c90517
+
c90517
+                if (error != NULL) {
c90517
+                        g_warning ("could not read active VT from kernel: %s", error->message);
c90517
+                }
c90517
+                switch (status) {
c90517
+                        case G_IO_STATUS_ERROR:
c90517
+                            return G_SOURCE_REMOVE;
c90517
+                        case G_IO_STATUS_EOF:
c90517
+                            return G_SOURCE_REMOVE;
c90517
+                        case G_IO_STATUS_AGAIN:
c90517
+                            return G_SOURCE_CONTINUE;
c90517
+                        case G_IO_STATUS_NORMAL:
c90517
+                            break;
c90517
+                }
c90517
+        }
c90517
+
c90517
+        if ((condition & G_IO_ERR) || (condition & G_IO_HUP))
c90517
+                return G_SOURCE_REMOVE;
c90517
+
c90517
+        if (tty_of_active_vt == NULL)
c90517
+                return G_SOURCE_CONTINUE;
c90517
+
c90517
+        g_strchomp (tty_of_active_vt);
c90517
+
c90517
+        /* don't do anything if we're on the same VT we were before */
c90517
+        if (g_strcmp0 (tty_of_active_vt, factory->priv->tty_of_active_vt) == 0)
c90517
+                return G_SOURCE_CONTINUE;
c90517
+
c90517
+        tty_of_previous_vt = g_steal_pointer (&factory->priv->tty_of_active_vt);
c90517
+        factory->priv->tty_of_active_vt = g_steal_pointer (&tty_of_active_vt);
c90517
+
c90517
+        /* if the old VT was running a wayland login screen kill it
c90517
+         */
c90517
+        if (gdm_get_login_window_session_id ("seat0", &login_session_id)) {
c90517
+                unsigned int vt;
c90517
+
c90517
+                ret = sd_session_get_vt (login_session_id, &vt;;
c90517
+                if (ret == 0 && vt != 0) {
c90517
+                        g_autofree char *tty_of_login_window_vt = NULL;
c90517
+
c90517
+                        tty_of_login_window_vt = g_strdup_printf ("tty%u", vt);
c90517
+
c90517
+                        if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) {
c90517
+                                GdmDisplayStore *store;
c90517
+                                GdmDisplay *display;
c90517
+
c90517
+                                store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
c90517
+                                display = gdm_display_store_find (store,
c90517
+                                                                  lookup_by_session_id,
c90517
+                                                                  (gpointer) login_session_id);
c90517
+
c90517
+                                if (display != NULL)
c90517
+                                        maybe_stop_greeter_display (display);
c90517
+                        }
c90517
+                }
c90517
+        }
c90517
+
c90517
+        /* if user jumped back to initial vt and it's empty put a login screen
c90517
+         * on it (unless a login screen is already running elsewhere, then
c90517
+         * jump to that login screen)
c90517
+         */
c90517
+        if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) {
c90517
+                return G_SOURCE_CONTINUE;
c90517
+        }
c90517
+
c90517
+        ret = sd_seat_get_active ("seat0", &active_session_id, NULL);
c90517
+
c90517
+        if (ret == 0) {
c90517
+                g_autofree char *state = NULL;
c90517
+                ret = sd_session_get_state (active_session_id, &state);
c90517
+
c90517
+                /* if there's something already running on the active VT then bail */
c90517
+                if (ret == 0 && g_strcmp0 (state, "closing") != 0)
c90517
+                        return G_SOURCE_CONTINUE;
c90517
+        }
c90517
+
c90517
+        if (gdm_local_display_factory_use_wayland ())
c90517
+                session_type = "wayland";
c90517
+
c90517
+        create_display (factory, "seat0", session_type, TRUE);
c90517
+
c90517
+        return G_SOURCE_CONTINUE;
c90517
+}
c90517
+#endif
c90517
+
c90517
 static void
c90517
 gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
c90517
 {
c90517
+        g_autoptr (GIOChannel) io_channel = NULL;
c90517
+
c90517
         factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection,
c90517
                                                                          "org.freedesktop.login1",
c90517
                                                                          "org.freedesktop.login1.Manager",
c90517
                                                                          "SeatNew",
c90517
                                                                          "/org/freedesktop/login1",
c90517
                                                                          NULL,
c90517
                                                                          G_DBUS_SIGNAL_FLAGS_NONE,
c90517
                                                                          on_seat_new,
c90517
                                                                          g_object_ref (factory),
c90517
                                                                          g_object_unref);
c90517
         factory->priv->seat_removed_id = g_dbus_connection_signal_subscribe (factory->priv->connection,
c90517
                                                                              "org.freedesktop.login1",
c90517
                                                                              "org.freedesktop.login1.Manager",
c90517
                                                                              "SeatRemoved",
c90517
                                                                              "/org/freedesktop/login1",
c90517
                                                                              NULL,
c90517
                                                                              G_DBUS_SIGNAL_FLAGS_NONE,
c90517
                                                                              on_seat_removed,
c90517
                                                                              g_object_ref (factory),
c90517
                                                                              g_object_unref);
c90517
+
c90517
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
c90517
+        io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL);
c90517
+
c90517
+        if (io_channel != NULL) {
c90517
+                factory->priv->active_vt_watch_id =
c90517
+                        g_io_add_watch (io_channel,
c90517
+                                        G_IO_PRI,
c90517
+                                        (GIOFunc)
c90517
+                                        on_vt_changed,
c90517
+                                        factory);
c90517
+        }
c90517
+#endif
c90517
 }
c90517
 
c90517
 static void
c90517
 gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
c90517
 {
c90517
         if (factory->priv->seat_new_id) {
c90517
                 g_dbus_connection_signal_unsubscribe (factory->priv->connection,
c90517
                                                       factory->priv->seat_new_id);
c90517
                 factory->priv->seat_new_id = 0;
c90517
         }
c90517
         if (factory->priv->seat_removed_id) {
c90517
                 g_dbus_connection_signal_unsubscribe (factory->priv->connection,
c90517
                                                       factory->priv->seat_removed_id);
c90517
                 factory->priv->seat_removed_id = 0;
c90517
         }
c90517
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
c90517
+        if (factory->priv->active_vt_watch_id) {
c90517
+                g_source_remove (factory->priv->active_vt_watch_id);
c90517
+                factory->priv->active_vt_watch_id = 0;
c90517
+        }
c90517
+
c90517
+        g_clear_pointer (&factory->priv->tty_of_active_vt, g_free);
c90517
+#endif
c90517
 }
c90517
 
c90517
 static void
c90517
 on_display_added (GdmDisplayStore        *display_store,
c90517
                   const char             *id,
c90517
                   GdmLocalDisplayFactory *factory)
c90517
 {
c90517
         GdmDisplay *display;
c90517
 
c90517
         display = gdm_display_store_lookup (display_store, id);
c90517
 
c90517
         if (display != NULL) {
c90517
                 g_signal_connect_object (display, "notify::status",
c90517
                                          G_CALLBACK (on_display_status_changed),
c90517
                                          factory,
c90517
                                          0);
c90517
 
c90517
                 g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
c90517
         }
c90517
 }
c90517
 
c90517
 static void
c90517
 on_display_removed (GdmDisplayStore        *display_store,
c90517
                     GdmDisplay             *display,
c90517
                     GdmLocalDisplayFactory *factory)
c90517
 {
c90517
         g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), factory);
c90517
         g_object_weak_unref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
c90517
 }
c90517
 
c90517
-- 
c90517
2.26.0
c90517