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

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