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

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