[gdm/benzea/wait-seat-graphical] local-display-factory: Wait for seats to become graphical




commit 219c00f6ca3d17edf6d016b50b170740daa2dfe7
Author: Benjamin Berg <bberg redhat com>
Date:   Tue Feb 9 15:43:05 2021 +0100

    local-display-factory: Wait for seats to become graphical
    
    It may happen that seats are not graphical initially because the DRM
    device is not ready yet. In that case, ignore the seat and wait for the
    CanGraphical property notification in order to add it at that point.
    
    However, there seem to be some rare cases where CanGraphical will never
    turn TRUE. To catch these, launch "udevadm settle", and start assuming
    that "seat0" is graphical after it returns.
    
    Fixes: #662

 daemon/gdm-local-display-factory.c | 118 +++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
---
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 8c898215d..26a129cdf 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -62,6 +62,10 @@ struct _GdmLocalDisplayFactory
 
         guint            seat_new_id;
         guint            seat_removed_id;
+        guint            seat_prop_changed_id;
+
+        gboolean         udev_settle_started;
+        gboolean         udev_settled;
 
 #if defined(ENABLE_USER_DISPLAY_SERVER)
         unsigned int     active_vt;
@@ -450,17 +454,72 @@ lookup_prepared_display_by_seat_id (const char *id,
         return lookup_by_seat_id (id, display, user_data);
 }
 
+static void
+udevadm_settle_cb (GPid pid,
+                   gint status,
+                   gpointer user_data)
+{
+        GdmLocalDisplayFactory *factory = user_data;
+
+        if (status != 0)
+                g_warning ("udevadm settle finished with status %d", status);
+        g_debug ("udevadm settle finished. Assuming seat0 is graphical from now on and re-adding it.",
+                 status);
+        factory->udev_settled = TRUE;
+
+        /* Simply try to re-add seat0. If it is there already (i.e. CanGraphical
+         * turned TRUE, then we'll find it and it will not be created again.
+         */
+        create_display (factory, "seat0", 0);
+}
+
 static GdmDisplay *
 create_display (GdmLocalDisplayFactory *factory,
                 const char             *seat_id,
                 int                     failures)
 {
         gboolean is_initial;
+        int can_graphical;
         const char *session_type = NULL;
         GdmDisplayStore *store;
         GdmDisplay      *display = NULL;
         g_autofree char *login_session_id = NULL;
 
+        can_graphical = sd_seat_can_graphical (seat_id);
+        if (can_graphical < 0)
+                return NULL;
+
+        /* If seat0 is not graphical, then start udevadm settle if needed. */
+        if (!can_graphical && !factory->udev_settle_started && g_strcmp0 (seat_id, "seat0") == 0) {
+                g_autoptr(GError) error = NULL;
+                gchar * argv[] = { "udevadm", "settle", NULL };
+                GPid pid;
+
+                factory->udev_settle_started = TRUE;
+
+                g_debug ("Starting udevadm settle to wait for all devices.");
+
+                if (!g_spawn_async (NULL, argv, NULL,
+                                    G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH | 
G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
+                                    NULL, NULL,
+                                    &pid,
+                                    &error)) {
+                        g_warning ("Failed to spawn udevadm settle: %s", error->message);
+                        return NULL;
+                }
+
+                g_child_watch_add (pid, udevadm_settle_cb, factory);
+
+                return NULL;
+        }
+
+        /* If seath0 is not graphical and */
+        if (!can_graphical && factory->udev_settled && g_strcmp0 (seat_id, "seat0") == 0)
+                can_graphical = TRUE;
+
+        if (!can_graphical)
+                return NULL;
+
         if (g_strcmp0 (seat_id, "seat0") == 0) {
                 is_initial = TRUE;
                 if (failures == 0 && gdm_local_display_factory_use_wayland ())
@@ -599,6 +658,7 @@ on_seat_new (GDBusConnection *connection,
         const char *seat;
 
         g_variant_get (parameters, "(&s&o)", &seat, NULL);
+
         create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, 0);
 }
 
@@ -617,6 +677,49 @@ on_seat_removed (GDBusConnection *connection,
         delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
 }
 
+static void
+on_seat_prop_changed (GDBusConnection *connection,
+                      const gchar     *sender_name,
+                      const gchar     *object_path,
+                      const gchar     *interface_name,
+                      const gchar     *signal_name,
+                      GVariant        *parameters,
+                      gpointer         user_data)
+{
+        const gchar *seat = NULL;
+        g_autoptr(GVariant) changed_props = NULL;
+        g_autoptr(GVariant) changed_prop = NULL;
+        g_autofree const gchar * const *invalidated_props = NULL;
+        gboolean changed = FALSE;
+        int res;
+
+        /* Extract seat id, i.e. the last element of the object path. */
+        seat = strrchr (object_path, '/');
+        if (seat == NULL)
+                return;
+        seat += 1;
+
+        g_variant_get (parameters, "(s@a{sv}^a&s)", NULL, &changed_props, &invalidated_props);
+
+        changed_prop = g_variant_lookup_value (changed_props, "CanGraphical", NULL);
+        if (changed_prop)
+                changed = TRUE;
+        if (!changed && g_strv_contains (invalidated_props, "CanGraphical"))
+                changed = TRUE;
+
+        if (!changed)
+                return;
+
+        res = sd_seat_can_graphical (seat);
+        if (res < 0)
+                return;
+
+        if (res)
+                create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, 0);
+        else
+                delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+}
+
 static gboolean
 lookup_by_session_id (const char *id,
                       GdmDisplay *display,
@@ -851,6 +954,16 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
                                                                              on_seat_removed,
                                                                              g_object_ref (factory),
                                                                              g_object_unref);
+        factory->seat_prop_changed_id = g_dbus_connection_signal_subscribe (factory->connection,
+                                                                            "org.freedesktop.login1",
+                                                                            
"org.freedesktop.DBus.Properties",
+                                                                            "PropertiesChanged",
+                                                                            NULL,
+                                                                            "org.freedesktop.login1.Seat",
+                                                                            
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
+                                                                            on_seat_prop_changed,
+                                                                            g_object_ref (factory),
+                                                                            g_object_unref);
 
 #if defined(ENABLE_USER_DISPLAY_SERVER)
         io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL);
@@ -879,6 +992,11 @@ gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
                                                       factory->seat_removed_id);
                 factory->seat_removed_id = 0;
         }
+        if (factory->seat_prop_changed_id) {
+                g_dbus_connection_signal_unsubscribe (factory->connection,
+                                                      factory->seat_prop_changed_id);
+                factory->seat_prop_changed_id = 0;
+        }
 #if defined(ENABLE_USER_DISPLAY_SERVER)
         if (factory->active_vt_watch_id) {
                 g_source_remove (factory->active_vt_watch_id);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]