From 6e9fb3fb4df7d05c803cc01fe638eb9cf88c4beb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2011 01:32:54 +0200 Subject: local-display-factory: subscribe to new seats being created and removed logind will notify us when ever a new seat becomes available in the system, or an existing seat is removed. We simply create a new display for each seat showing up and remove a display when a seat goes away. --- daemon/Makefile.am | 1 + daemon/gdm-local-display-factory.c | 239 ++++++++++++++++++++++++++++++++++--- 2 files changed, 224 insertions(+), 16 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 8f8eedfe..ffbaf9b0 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -363,6 +363,7 @@ gdm_binary_LDADD = \ $(DAEMON_LIBS) \ $(XDMCP_LIBS) \ $(LIBWRAP_LIBS) \ + $(SYSTEMD_LIBS) \ $(NULL) if WITH_CONSOLE_KIT diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c index 349c7b2c..988b2ff9 100644 --- a/daemon/gdm-local-display-factory.c +++ b/daemon/gdm-local-display-factory.c @@ -27,6 +27,13 @@ #include #include +#include +#include + +#ifdef WITH_SYSTEMD +#include +#endif + #include "gdm-display-factory.h" #include "gdm-local-display-factory.h" #include "gdm-local-display-factory-glue.h" @@ -64,7 +71,8 @@ static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryC static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); static void gdm_local_display_factory_finalize (GObject *object); -static GdmDisplay *create_display (GdmLocalDisplayFactory *factory); +static GdmDisplay *create_display (GdmLocalDisplayFactory *factory, + const char *seat_id); static gpointer local_display_factory_object = NULL; @@ -282,6 +290,7 @@ on_static_display_status_changed (GdmDisplay *display, int status; GdmDisplayStore *store; int num; + char *seat_id = NULL; num = -1; gdm_display_get_x11_display_number (display, &num, NULL); @@ -289,6 +298,8 @@ on_static_display_status_changed (GdmDisplay *display, store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + g_object_get (display, "seat-id", &seat_id, NULL); + status = gdm_display_get_status (display); g_debug ("GdmLocalDisplayFactory: static display status changed: %d", status); @@ -300,7 +311,7 @@ on_static_display_status_changed (GdmDisplay *display, gdm_display_store_remove (store, display); /* reset num failures */ factory->priv->num_failures = 0; - create_display (factory); + create_display (factory, seat_id); break; case GDM_DISPLAY_FAILED: /* leave the display number in factory->priv->displays @@ -313,7 +324,7 @@ on_static_display_status_changed (GdmDisplay *display, /* FIXME: should monitor hardware changes to try again when seats change */ } else { - create_display (factory); + create_display (factory, seat_id); } break; case GDM_DISPLAY_UNMANAGED: @@ -326,13 +337,44 @@ on_static_display_status_changed (GdmDisplay *display, g_assert_not_reached (); break; } + + g_free (seat_id); +} + +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", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; } static GdmDisplay * -create_display (GdmLocalDisplayFactory *factory) +create_display (GdmLocalDisplayFactory *factory, + const char *seat_id) { - GdmDisplay *display; - guint32 num; + GdmDisplayStore *store; + GdmDisplay *display; + guint32 num; + + /* Ensure we don't create the same display more than once */ + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + if (display != NULL) { + return NULL; + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); num = take_next_display_number (factory); @@ -342,8 +384,7 @@ create_display (GdmLocalDisplayFactory *factory) display = gdm_static_display_new (num); #endif - /* FIXME: don't hardcode seat1? */ - g_object_set (display, "seat-id", CK_SEAT1_PATH, NULL); + g_object_set (display, "seat-id", seat_id, NULL); g_signal_connect (display, "notify::status", @@ -362,24 +403,182 @@ create_display (GdmLocalDisplayFactory *factory) return display; } +#ifdef WITH_SYSTEMD + +static void +delete_display (GdmLocalDisplayFactory *factory, + const char *seat_id) { + + GdmDisplayStore *store; + + g_debug ("GdmLocalDisplayFactory: Removing displays on seat %s", seat_id); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_foreach_remove (store, lookup_by_seat_id, (gpointer) seat_id); +} + +static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory) +{ + DBusError error; + DBusMessage *message, *reply; + DBusMessageIter iter, sub, sub2; + + dbus_error_init (&error); + + message = dbus_message_new_method_call ( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats"); + if (message == NULL) { + g_warning ("GdmLocalDisplayFactory: Failed to allocate message"); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (factory->priv->connection), message, -1, &error); + dbus_message_unref (message); + + if (reply == NULL) { + g_warning ("GdmLocalDisplayFactory: Failed to issue method call: %s", error.message); + dbus_error_free (&error); + return FALSE; + } + + if (!dbus_message_iter_init (reply, &iter) || + dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRUCT) { + g_warning ("GdmLocalDisplayFactory: Failed to parse reply."); + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_iter_recurse (&iter, &sub); + + while (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_INVALID) { + const char *seat; + + if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_STRUCT) { + g_warning ("GdmLocalDisplayFactory: Failed to parse reply."); + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_iter_recurse (&sub, &sub2); + + if (dbus_message_iter_get_arg_type (&sub2) != DBUS_TYPE_STRING) { + g_warning ("GdmLocalDisplayFactory: Failed to parse reply."); + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_iter_get_basic (&sub2, &seat); + create_display (factory, seat); + + dbus_message_iter_next (&sub); + } + + dbus_message_unref (reply); + return TRUE; +} + +static DBusHandlerResult +on_seat_signal (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GdmLocalDisplayFactory *factory = user_data; + DBusError error; + + dbus_error_init (&error); + + if (dbus_message_is_signal (message, "org.freedesktop.login1.Manager", "SeatNew") || + dbus_message_is_signal (message, "org.freedesktop.login1.Manager", "SeatRemoved")) { + const char *seat; + + dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, &seat, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set (&error)) { + g_warning ("GdmLocalDisplayFactory: Failed to decode seat message: %s", error.message); + dbus_error_free (&error); + } else { + + if (strcmp (dbus_message_get_member (message), "SeatNew") == 0) { + create_display (factory, seat); + } else { + delete_display (factory, seat); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void +gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) +{ + DBusError error; + + dbus_error_init (&error); + + dbus_bus_add_match (dbus_g_connection_get_connection (factory->priv->connection), + "type='signal'," + "sender='org.freedesktop.login1'," + "path='/org/freedesktop/login1'," + "interface='org.freedesktop.login1.Manager'," + "member='SeatNew'", + &error); + + if (dbus_error_is_set (&error)) { + g_warning ("GdmLocalDisplayFactory: Failed to add match for SeatNew: %s", error.message); + dbus_error_free (&error); + } + + dbus_bus_add_match (dbus_g_connection_get_connection (factory->priv->connection), + "type='signal'," + "sender='org.freedesktop.login1'," + "path='/org/freedesktop/login1'," + "interface='org.freedesktop.login1.Manager'," + "member='SeatRemoved'", + &error); + + if (dbus_error_is_set (&error)) { + g_warning ("GdmLocalDisplayFactory: Failed to add match for SeatNew: %s", error.message); + dbus_error_free (&error); + } + + dbus_connection_add_filter (dbus_g_connection_get_connection (factory->priv->connection), on_seat_signal, factory, NULL); +} + +static void +gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory) +{ + dbus_connection_remove_filter (dbus_g_connection_get_connection (factory->priv->connection), on_seat_signal, factory); +} + +#endif + static gboolean gdm_local_display_factory_start (GdmDisplayFactory *base_factory) { - gboolean ret; GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (base_factory); GdmDisplay *display; g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); - ret = TRUE; - - /* FIXME: use seat configuration */ - display = create_display (factory); - if (display == NULL) { - ret = FALSE; +#ifdef WITH_SYSTEMD + if (sd_booted () > 0) { + gdm_local_display_factory_start_monitor (factory); + return gdm_local_display_factory_sync_seats (factory); } +#endif - return ret; + /* On ConsoleKit just create Seat1, and that's it. */ + display = create_display (factory, CK_SEAT1_PATH); + + return display != NULL; } static gboolean @@ -389,6 +588,10 @@ gdm_local_display_factory_stop (GdmDisplayFactory *base_factory) g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); +#ifdef WITH_SYSTEMD + gdm_local_display_factory_stop_monitor (factory); +#endif + return TRUE; } @@ -499,6 +702,10 @@ gdm_local_display_factory_finalize (GObject *object) g_hash_table_destroy (factory->priv->displays); +#ifdef WITH_SYSTEMD + gdm_local_display_factory_stop_monitor (factory); +#endif + G_OBJECT_CLASS (gdm_local_display_factory_parent_class)->finalize (object); } -- cgit v1.2.1