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




commit 8b22d5f17208271f5f7897926682178c9d42e800
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 | 120 +++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
---
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 8c898215d..a2a752e0d 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,74 @@ 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 ("GdmLocalDisplayFactory: udevadm settle finished with status %d", status);
+        g_debug ("GdmLocalDisplayFactory: udevadm settle finished.",
+                 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 ("GdmLocalDisplayFactory: 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 ("GdmLocalDisplayFactory: 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) {
+                g_warning ("GdmLocalDisplayFactory: Assuming we can use seat0 even though it is not 
graphical!");
+                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 +660,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 +679,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 +956,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 +994,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]