summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2021-02-09 15:43:05 +0100
committerBenjamin Berg <bberg@redhat.com>2021-02-11 15:12:45 +0100
commitec305406b92e2bfaf8cc299ddc06113c614e1a63 (patch)
treedb207c01eba1a643c9456e5fc54e4456f1f82b86
parent2466f7dea5fe63c22d4f5f937732fba6597932ae (diff)
downloadgdm-benzea/wait-seat-graphical.tar.gz
local-display-factory: Wait for seats to become graphicalbenzea/wait-seat-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
-rw-r--r--daemon/gdm-local-display-factory.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 8c898215..344a074d 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 udev is settled and this is seat0 then assume we can use it */
+ 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);