Blob Blame History Raw
From a539080c470125871aa1774318a0b5aca14ce91d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 25 Sep 2018 10:59:37 -0400
Subject: [PATCH 32/48] manager: rework how autologin is figured out

At the moment we decide whether or not to perform autologin, by
looking at if the display is the initial VT display and if autologin
hasn't been started before.

That isn't going to work in the future when autologin is started
on a non-initial vt.

This commit changes GDM to instead check if the seat is seat0, and
if autologin hasn't run before, before deciding to do autologin.
---
 daemon/gdm-manager.c | 46 ++++++++++++++++++++++++++++++++------------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index fb7b1ec4b..228cec6ff 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -51,75 +51,76 @@
 #include "gdm-session.h"
 #include "gdm-session-record.h"
 #include "gdm-settings-direct.h"
 #include "gdm-settings-keys.h"
 #include "gdm-xdmcp-display-factory.h"
 #include "gdm-xdmcp-chooser-display.h"
 
 #define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate))
 
 #define GDM_DBUS_PATH             "/org/gnome/DisplayManager"
 #define GDM_MANAGER_PATH          GDM_DBUS_PATH "/Manager"
 #define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays"
 
 #define INITIAL_SETUP_USERNAME "gnome-initial-setup"
 
 typedef struct
 {
         GdmManager *manager;
         GdmSession *session;
         char *service_name;
         guint idle_id;
 } StartUserSessionOperation;
 
 struct GdmManagerPrivate
 {
         GdmDisplayStore        *display_store;
         GdmLocalDisplayFactory *local_factory;
 #ifdef HAVE_LIBXDMCP
         GdmXdmcpDisplayFactory *xdmcp_factory;
 #endif
+        GdmDisplay             *automatic_login_display;
         GList                  *user_sessions;
         GHashTable             *transient_sessions;
         GHashTable             *open_reauthentication_requests;
         gboolean                xdmcp_enabled;
 
         gboolean                started;
         gboolean                show_local_greeter;
 
         GDBusConnection          *connection;
         GDBusObjectManagerServer *object_manager;
 
 #ifdef  WITH_PLYMOUTH
         guint                     plymouth_is_running : 1;
 #endif
-        guint                     ran_once : 1;
+        guint                     did_automatic_login : 1;
 };
 
 enum {
         PROP_0,
         PROP_XDMCP_ENABLED,
         PROP_SHOW_LOCAL_GREETER
 };
 
 enum {
         DISPLAY_ADDED,
         DISPLAY_REMOVED,
         LAST_SIGNAL
 };
 
 typedef enum {
         SESSION_RECORD_LOGIN,
         SESSION_RECORD_LOGOUT,
         SESSION_RECORD_FAILED,
 } SessionRecord;
 
 static guint signals [LAST_SIGNAL] = { 0, };
 
 static void     gdm_manager_class_init  (GdmManagerClass *klass);
 static void     gdm_manager_init        (GdmManager      *manager);
 static void     gdm_manager_dispose     (GObject         *object);
 
 static GdmSession *create_user_session_for_display (GdmManager *manager,
                                                     GdmDisplay *display,
                                                     uid_t       allowed_user);
 static void     start_user_session (GdmManager                *manager,
@@ -1415,67 +1416,74 @@ typedef struct {
 static void
 destroy_username_lookup_operation (UsernameLookupOperation *operation)
 {
         g_object_unref (operation->manager);
         g_object_unref (operation->display);
         g_free (operation->username);
         g_free (operation);
 }
 
 static void
 on_user_is_loaded_changed (ActUser                 *user,
                            GParamSpec              *pspec,
                            UsernameLookupOperation *operation)
 {
         if (act_user_is_loaded (user)) {
                 set_up_automatic_login_session_if_user_exists (operation->manager, operation->display, user);
                 g_signal_handlers_disconnect_by_func (G_OBJECT (user),
                                                       G_CALLBACK (on_user_is_loaded_changed),
                                                       operation);
                 destroy_username_lookup_operation (operation);
         }
 }
 
 static void
 set_up_session (GdmManager *manager,
                 GdmDisplay *display)
 {
         ActUserManager *user_manager;
         ActUser *user;
         gboolean loaded;
-        gboolean is_initial_display = FALSE;
+        gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE;
         gboolean autologin_enabled = FALSE;
+        g_autofree char *seat_id = NULL;
         char *username = NULL;
 
-        g_object_get (G_OBJECT (display), "is-initial", &is_initial_display, NULL);
+        g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL);
+
+        if (g_strcmp0 (seat_id, "seat0") == 0)
+                seat_can_autologin = TRUE;
+
+        if (manager->priv->did_automatic_login || manager->priv->automatic_login_display != NULL)
+                seat_did_autologin = TRUE;
 
-        if (!manager->priv->ran_once && is_initial_display)
+        if (seat_can_autologin && !seat_did_autologin)
                 autologin_enabled = get_automatic_login_details (manager, &username);
 
         if (!autologin_enabled) {
                 g_free (username);
 
 #ifdef HAVE_LIBXDMCP
                 if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) {
                         set_up_chooser_session (manager, display);
                         return;
                 }
 #endif
 
                 set_up_greeter_session (manager, display);
                 return;
         }
 
         /* Check whether the user really exists before committing to autologin. */
         user_manager = act_user_manager_get_default ();
         user = act_user_manager_get_user (user_manager, username);
         g_object_get (user_manager, "is-loaded", &loaded, NULL);
 
         if (loaded) {
                 set_up_automatic_login_session_if_user_exists (manager, display, user);
         } else {
                 UsernameLookupOperation *operation;
 
                 operation = g_new (UsernameLookupOperation, 1);
                 operation->manager = g_object_ref (manager);
                 operation->display = g_object_ref (display);
                 operation->username = username;
@@ -1512,62 +1520,72 @@ on_display_status_changed (GdmDisplay *display,
                       "session-type", &session_type,
                       NULL);
 
         status = gdm_display_get_status (display);
 
         switch (status) {
                 case GDM_DISPLAY_PREPARED:
                 case GDM_DISPLAY_MANAGED:
                         if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) ||
                             (display_number != -1 && status == GDM_DISPLAY_MANAGED)) {
                                 char *session_class;
 
                                 g_object_get (display,
                                               "session-class", &session_class,
                                               NULL);
                                 if (g_strcmp0 (session_class, "greeter") == 0)
                                         set_up_session (manager, display);
                                 g_free (session_class);
                         }
                         break;
                 case GDM_DISPLAY_FAILED:
                 case GDM_DISPLAY_UNMANAGED:
                 case GDM_DISPLAY_FINISHED:
 #ifdef WITH_PLYMOUTH
                         if (quit_plymouth) {
                                 plymouth_quit_without_transition ();
                                 manager->priv->plymouth_is_running = FALSE;
                         }
 #endif
 
-                        if (!doing_initial_setup && (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0)) {
-                                manager->priv->ran_once = TRUE;
+                        if (display == manager->priv->automatic_login_display) {
+                                g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
+                                manager->priv->did_automatic_login = TRUE;
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+                                if (g_strcmp0 (session_type, "wayland") != 0 && status == GDM_DISPLAY_FAILED) {
+                                        /* we're going to fall back to X11, so try to autologin again
+                                         */
+                                        manager->priv->did_automatic_login = FALSE;
+                                }
+#endif
                         }
                         break;
                 default:
                         break;
         }
 
 }
 
 static void
 on_display_removed (GdmDisplayStore *display_store,
                     GdmDisplay      *display,
                     GdmManager      *manager)
 {
         char    *id;
 
         gdm_display_get_id (display, &id, NULL);
         g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
         g_free (id);
 
         g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager);
 
         g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display);
 }
 
 static void
 destroy_start_user_session_operation (StartUserSessionOperation *operation)
 {
         g_object_set_data (G_OBJECT (operation->session),
                            "start-user-session-operation",
                            NULL);
@@ -1621,132 +1639,134 @@ create_display_for_user_session (GdmManager *self,
                                  const char *session_id)
 {
         GdmDisplay *display;
         /* at the moment we only create GdmLocalDisplay objects on seat0 */
         const char *seat_id = "seat0";
 
         display = gdm_local_display_new ();
 
         g_object_set (G_OBJECT (display),
                       "session-class", "user",
                       "seat-id", seat_id,
                       "session-id", session_id,
                       NULL);
         gdm_display_store_add (self->priv->display_store,
                                display);
         g_object_set_data (G_OBJECT (session), "gdm-display", display);
         g_object_set_data_full (G_OBJECT (display),
                                 "gdm-user-session",
                                 g_object_ref (session),
                                 (GDestroyNotify)
                                 clean_user_session);
 }
 
 static gboolean
 on_start_user_session (StartUserSessionOperation *operation)
 {
         GdmManager *self = operation->manager;
         gboolean migrated;
         gboolean fail_if_already_switched = TRUE;
         gboolean doing_initial_setup = FALSE;
-        gboolean starting_user_session_right_away = TRUE;
         GdmDisplay *display;
         const char *session_id;
 
         g_debug ("GdmManager: start or jump to session");
 
         /* If there's already a session running, jump to it.
          * If the only session running is the one we just opened,
          * start a session on it.
          */
         migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched);
 
         g_debug ("GdmManager: migrated: %d", migrated);
         if (migrated) {
                 /* We don't stop the manager here because
                    when Xorg exits it switches to the VT it was
                    started from.  That interferes with fast
                    user switching. */
                 gdm_session_reset (operation->session);
                 destroy_start_user_session_operation (operation);
                 goto out;
         }
 
         display = get_display_for_user_session (operation->session);
 
         g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL);
 
         session_id = gdm_session_get_conversation_session_id (operation->session,
                                                               operation->service_name);
 
         if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
                 /* In this case, the greeter's display is morphing into
                  * the user session display. Kill the greeter on this session
                  * and let the user session follow the same display. */
                 gdm_display_stop_greeter_session (display);
                 g_object_set (G_OBJECT (display),
                                 "session-class", "user",
                                 "session-id", session_id,
                                 NULL);
         } else {
                 uid_t allowed_uid;
 
                 g_object_ref (display);
                 if (doing_initial_setup) {
                         g_debug ("GdmManager: closing down initial setup display");
                         gdm_display_stop_greeter_session (display);
                         gdm_display_unmanage (display);
                         gdm_display_finish (display);
                 } else {
                         g_debug ("GdmManager: session has its display server, reusing our server for another login screen");
                 }
 
                 /* The user session is going to follow the session worker
                  * into the new display. Untie it from this display and
                  * create a new session for a future user login. */
                 allowed_uid = gdm_session_get_allowed_user (operation->session);
                 g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL);
                 g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL);
                 create_user_session_for_display (operation->manager, display, allowed_uid);
 
+                /* Give the user session a new display object for bookkeeping purposes */
+                create_display_for_user_session (operation->manager,
+                                                 operation->session,
+                                                 session_id);
+
                 if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) &&
                     !gdm_session_client_is_connected (operation->session)) {
                         /* remove the unused prepared greeter display since we're not going
                          * to have a greeter */
                         gdm_display_store_remove (self->priv->display_store, display);
                         g_object_unref (display);
-                }
 
-                /* Give the user session a new display object for bookkeeping purposes */
-                create_display_for_user_session (operation->manager,
-                                                 operation->session,
-                                                 session_id);
+                        self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display");
+                        g_object_add_weak_pointer (G_OBJECT (display), (gpointer *) &self->priv->automatic_login_display);
+                }
         }
 
         start_user_session (operation->manager, operation);
 
  out:
         return G_SOURCE_REMOVE;
 }
 
 static void
 queue_start_user_session (GdmManager *manager,
                           GdmSession *session,
                           const char *service_name)
 {
         StartUserSessionOperation *operation;
 
         operation = g_slice_new0 (StartUserSessionOperation);
         operation->manager = manager;
         operation->session = g_object_ref (session);
         operation->service_name = g_strdup (service_name);
 
         operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation);
         g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation);
 }
 
 static void
 start_user_session_if_ready (GdmManager *manager,
                              GdmSession *session,
                              const char *service_name)
 {
         gboolean start_when_ready;
@@ -2601,60 +2621,62 @@ unexport_display (const char *id,
                   GdmDisplay *display,
                   GdmManager *manager)
 {
         if (!g_dbus_connection_is_closed (manager->priv->connection))
                 g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
 }
 
 static void
 finish_display (const char *id,
                 GdmDisplay *display,
                 GdmManager *manager)
 {
         gdm_display_stop_greeter_session (display);
         if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED)
                 gdm_display_unmanage (display);
         gdm_display_finish (display);
 }
 
 static void
 gdm_manager_dispose (GObject *object)
 {
         GdmManager *manager;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GDM_IS_MANAGER (object));
 
         manager = GDM_MANAGER (object);
 
         g_return_if_fail (manager->priv != NULL);
 
+        g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
 #ifdef HAVE_LIBXDMCP
         g_clear_object (&manager->priv->xdmcp_factory);
 #endif
         g_clear_object (&manager->priv->local_factory);
         g_clear_pointer (&manager->priv->open_reauthentication_requests,
                          (GDestroyNotify)
                          g_hash_table_unref);
         g_clear_pointer (&manager->priv->transient_sessions,
                          (GDestroyNotify)
                          g_hash_table_unref);
 
         g_list_foreach (manager->priv->user_sessions,
                         (GFunc) gdm_session_close,
                         NULL);
         g_list_free_full (manager->priv->user_sessions, (GDestroyNotify) g_object_unref);
         manager->priv->user_sessions = NULL;
 
         g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
                                               G_CALLBACK (on_display_added),
                                               manager);
         g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
                                               G_CALLBACK (on_display_removed),
                                               manager);
 
         if (!g_dbus_connection_is_closed (manager->priv->connection)) {
                 gdm_display_store_foreach (manager->priv->display_store,
                                            (GdmDisplayStoreFunc)unexport_display,
                                            manager);
                 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (manager));
         }
-- 
2.26.0