Blob Blame History Raw
From 02429cabcddd3a12e0cddd975820b37e8440f1c7 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 22 May 2019 10:53:12 -0400
Subject: [PATCH] local-display-factory: don't spawn login screen if background
 session dies

At the moment gdm conjures up a login screen any time a user session
exits.

This is the right behavior if the user explicitly logs out, but if an
admin is killing a session on a background VT, then going to the login
screen is wrong.

This commit changes the code to detect when the killed session is in
the foreground, and only then bring up a login screen.
---
 daemon/gdm-local-display-factory.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index cf4f5095c..6856d30d0 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -227,137 +227,159 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact
 
         store_display (factory, display);
 
         if (! gdm_display_manage (display)) {
                 display = NULL;
                 goto out;
         }
 
         if (! gdm_display_get_id (display, id, NULL)) {
                 display = NULL;
                 goto out;
         }
 
         ret = TRUE;
  out:
         /* ref either held by store or not at all */
         g_object_unref (display);
 
         return ret;
 }
 
 static void
 on_display_status_changed (GdmDisplay             *display,
                            GParamSpec             *arg1,
                            GdmLocalDisplayFactory *factory)
 {
         int              status;
         GdmDisplayStore *store;
         int              num;
         char            *seat_id = NULL;
+        char            *session_id = NULL;
         char            *session_type = NULL;
         char            *session_class = NULL;
         gboolean         is_initial = TRUE;
         gboolean         is_local = TRUE;
+        int              ret;
 
         num = -1;
         gdm_display_get_x11_display_number (display, &num, NULL);
 
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
 
         g_object_get (display,
                       "seat-id", &seat_id,
+                      "session-id", &session_id,
                       "is-initial", &is_initial,
                       "is-local", &is_local,
                       "session-type", &session_type,
                       "session-class", &session_class,
                       NULL);
 
         status = gdm_display_get_status (display);
 
         g_debug ("GdmLocalDisplayFactory: display status changed: %d", status);
         switch (status) {
         case GDM_DISPLAY_FINISHED:
                 /* remove the display number from factory->priv->used_display_numbers
                    so that it may be reused */
                 if (num != -1) {
                         g_hash_table_remove (factory->priv->used_display_numbers, GUINT_TO_POINTER (num));
                 }
                 gdm_display_store_remove (store, display);
 
                 /* if this is a local display, recreate the display so
                  * a new login screen comes up if one is missing.
                  */
                 if (is_local && g_strcmp0 (session_class, "greeter") != 0) {
+                        g_autofree char *active_session = NULL;
+
                         /* reset num failures */
                         factory->priv->num_failures = 0;
 
-                        create_display (factory, seat_id, session_type, is_initial);
+                        ret = sd_seat_get_active (seat_id, &active_session, NULL);
+
+                        if (ret == 0) {
+                                g_autofree char *state = NULL;
+                                ret = sd_session_get_state (active_session, &state);
+                                if (ret != 0 ||
+                                    g_strcmp0 (state, "closing") == 0 ||
+                                    g_strcmp0 (active_session, session_id) == 0) {
+                                        g_clear_pointer (&active_session, free);
+                                }
+                        }
+
+                        /* If this died in the foreground leaving us on a blank vt,
+                           start a new login screen */
+                        if (!sd_seat_can_multi_session (seat_id) || active_session == NULL) {
+                                create_display (factory, seat_id, session_type, is_initial);
+                        }
                 }
                 break;
         case GDM_DISPLAY_FAILED:
                 /* leave the display number in factory->priv->used_display_numbers
                    so that it doesn't get reused */
                 gdm_display_store_remove (store, display);
 
                 /* Create a new equivalent display if it was static */
                 if (is_local) {
 
                         factory->priv->num_failures++;
 
                         if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) {
                                 /* oh shit */
                                 g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors");
                         } else {
 #ifdef ENABLE_WAYLAND_SUPPORT
                                 if (g_strcmp0 (session_type, "wayland") == 0) {
                                         g_free (session_type);
                                         session_type = NULL;
                                 }
 
 #endif
                                 create_display (factory, seat_id, session_type, is_initial);
                         }
                 }
                 break;
         case GDM_DISPLAY_UNMANAGED:
                 break;
         case GDM_DISPLAY_PREPARED:
                 break;
         case GDM_DISPLAY_MANAGED:
                 break;
         default:
                 g_assert_not_reached ();
                 break;
         }
 
         g_free (seat_id);
+        g_free (session_id);
         g_free (session_type);
         g_free (session_class);
 }
 
 static gboolean
 lookup_by_seat_id (const char *id,
                    GdmDisplay *display,
                    gpointer    user_data)
 {
         const char *looking_for = user_data;
         char *current;
         gboolean res;
 
         g_object_get (G_OBJECT (display), "seat-id", &current, NULL);
 
         res = g_strcmp0 (current, looking_for) == 0;
 
         g_free(current);
 
         return res;
 }
 
 static gboolean
 activate_session_id (GdmLocalDisplayFactory *self,
                      const char             *seat_id,
                      const char             *session_id)
 {
         GError *error = NULL;
         GVariant *reply;
 
-- 
2.21.0