]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - src/login1.c
Load all users only when really needed
[sojka/lightdm.git] / src / login1.c
index cd780c58999a691994a1d2b72d2d453470320513..dc60a492a02a09557ba842d0809a71210103e330 100644 (file)
  * license.
  */
 
+#include <string.h>
 #include <gio/gio.h>
 
 #include "login1.h"
 
-static gboolean
-check_login1 (void)
-{
-    GDBusProxy *proxy;
-    gchar *owner;
-    gboolean success;
-
-    proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-                                           G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-                                           NULL,
-                                           "org.freedesktop.login1",
-                                           "/org/freedesktop/login1",
-                                           "org.freedesktop.login1.Manager",
-                                           NULL,
-                                           NULL);
-    if (!proxy)
-        return FALSE;
+#define LOGIN1_SERVICE_NAME "org.freedesktop.login1"
+#define LOGIN1_OBJECT_NAME "/org/freedesktop/login1"
+#define LOGIN1_MANAGER_INTERFACE_NAME "org.freedesktop.login1.Manager"
+
+enum {
+    SEAT_ADDED,
+    SEAT_REMOVED,
+    LAST_SERVICE_SIGNAL
+};
+static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 };
+
+struct Login1ServicePrivate
+{
+    /* Connection to bus service is running on */
+    GDBusConnection *connection;
+
+    /* TRUE if have connected to service */
+    gboolean connected;
+
+    /* Seats the service is reporting */
+    GList *seats;
+
+    /* Handle to signal subscription */
+    guint signal_id;
+};
+
+enum {
+    CAN_GRAPHICAL_CHANGED,
+    ACTIVE_SESSION_CHANGED,
+    LAST_SEAT_SIGNAL
+};
+static guint seat_signals[LAST_SEAT_SIGNAL] = { 0 };
 
-    owner = g_dbus_proxy_get_name_owner (proxy);
-    g_object_unref (proxy);
+struct Login1SeatPrivate
+{
+    /* Connection to bus seat is running on */
+    GDBusConnection *connection;
+
+    /* Seat Id */
+    gchar *id;
+
+    /* D-Bus path for this seat */
+    gchar *path;
+
+    /* Handle to signal subscription */
+    guint signal_id;
 
-    success = (owner != NULL);
-    g_free (owner);
+    /* TRUE if can run a graphical display on this seat */
+    gboolean can_graphical;
 
-    return success;
+    /* TRUE if can do session switching */
+    gboolean can_multi_session;
+};
+
+G_DEFINE_TYPE (Login1Service, login1_service, G_TYPE_OBJECT);
+G_DEFINE_TYPE (Login1Seat, login1_seat, G_TYPE_OBJECT);
+
+static Login1Service *singleton = NULL;
+
+Login1Service *
+login1_service_get_instance (void)
+{
+    if (!singleton)
+        singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
+    return singleton;
 }
 
-gboolean
-login1_is_running (void)
+static void
+update_property (Login1Seat *seat, const gchar *name, GVariant *value)
+{
+    if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+    {
+        seat->priv->can_graphical = g_variant_get_boolean (value);
+        g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
+    }
+    else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE ("(so)")))
+    {
+        const gchar *login1_session_id;
+        g_variant_get (value, "(&so)", &login1_session_id, NULL);
+        g_signal_emit (seat, seat_signals[ACTIVE_SESSION_CHANGED], 0, login1_session_id);
+    }
+}
+
+static void
+seat_properties_changed_cb (GDBusConnection *connection,
+                            const gchar *sender_name,
+                            const gchar *object_path,
+                            const gchar *interface_name,
+                            const gchar *signal_name,
+                            GVariant *parameters,
+                            gpointer user_data)
+{
+    Login1Seat *seat = user_data;
+    GVariantIter *iter;
+    GVariantIter *invalidated_properties;
+    const gchar *name;
+    GVariant *value;
+
+    g_variant_get (parameters, "(sa{sv}as)", NULL, &iter, &invalidated_properties);
+    while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
+        update_property (seat, name, value);
+    g_variant_iter_free (iter);
+    while (g_variant_iter_loop (invalidated_properties, "&s", &name))
+    {
+        GVariant *result;
+        GError *error = NULL;
+
+        result = g_dbus_connection_call_sync (connection,
+                                              LOGIN1_SERVICE_NAME,
+                                              seat->priv->path,
+                                              "org.freedesktop.DBus.Properties",
+                                              "Get",
+                                              g_variant_new ("(ss)", "org.freedesktop.login1.Seat", name),
+                                              G_VARIANT_TYPE ("(v)"),
+                                              G_DBUS_CALL_FLAGS_NONE,
+                                              -1,
+                                              NULL,
+                                              &error);
+        if (error)
+            g_warning ("Error updating seat property %s: %s", name, error->message);
+        g_clear_error (&error);
+        if (result)
+        {
+            g_variant_get (result, "(v)", &value);
+            update_property (seat, name, value);
+            g_variant_unref (value);
+            g_variant_unref (result);
+        }
+    }
+    g_variant_iter_free (invalidated_properties);
+}
+
+static Login1Seat *
+add_seat (Login1Service *service, const gchar *id, const gchar *path)
 {
-    static gboolean have_checked = FALSE;
-    static gboolean is_running = FALSE;
+    Login1Seat *seat;
+    GVariant *result;
+    GError *error = NULL;
 
-    if (!have_checked)
+    seat = g_object_new (LOGIN1_SEAT_TYPE, NULL);
+    seat->priv->connection = g_object_ref (service->priv->connection);
+    seat->priv->id = g_strdup (id);
+    seat->priv->path = g_strdup (path);
+
+    seat->priv->signal_id = g_dbus_connection_signal_subscribe (seat->priv->connection,
+                                                                LOGIN1_SERVICE_NAME,
+                                                                "org.freedesktop.DBus.Properties",
+                                                                "PropertiesChanged",
+                                                                path,
+                                                                "org.freedesktop.login1.Seat",
+                                                                G_DBUS_SIGNAL_FLAGS_NONE,
+                                                                seat_properties_changed_cb,
+                                                                g_object_ref (seat),
+                                                                g_object_unref);
+
+    /* Get properties for this seat */
+    result = g_dbus_connection_call_sync (seat->priv->connection,
+                                          LOGIN1_SERVICE_NAME,
+                                          path,
+                                          "org.freedesktop.DBus.Properties",
+                                          "GetAll",
+                                          g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
+                                          G_VARIANT_TYPE ("(a{sv})"),
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          NULL,
+                                          &error);
+    if (error)
+        g_warning ("Failed to get seat properties: %s", error->message);
+    g_clear_error (&error);
+    if (result)
     {
-        have_checked = TRUE;
-        is_running = check_login1();
+        GVariantIter *properties;
+        const gchar *name;
+        GVariant *value;
+
+        g_variant_get (result, "(a{sv})", &properties);
+        while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
+        {
+            if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+                seat->priv->can_graphical = g_variant_get_boolean (value);
+            else if (strcmp (name, "CanMultiSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+                seat->priv->can_multi_session = g_variant_get_boolean (value);
+        }
+        g_variant_iter_free (properties);
+        g_variant_unref (result);
     }
 
-    return is_running;
+    service->priv->seats = g_list_append (service->priv->seats, seat);
+
+    return seat;
 }
 
-gchar *
-login1_get_session_id (void)
+static void
+signal_cb (GDBusConnection *connection,
+           const gchar *sender_name,
+           const gchar *object_path,
+           const gchar *interface_name,
+           const gchar *signal_name,
+           GVariant *parameters,
+           gpointer user_data)
+{
+    Login1Service *service = user_data;
+
+    if (strcmp (signal_name, "SeatNew") == 0)
+    {
+        const gchar *id, *path;
+        Login1Seat *seat;
+
+        g_variant_get (parameters, "(&s&o)", &id, &path);
+        seat = login1_service_get_seat (service, id);
+        if (!seat)
+        {
+            seat = add_seat (service, id, path);
+            g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
+        }
+    }
+    else if (strcmp (signal_name, "SeatRemoved") == 0)
+    {
+        const gchar *id, *path;
+        Login1Seat *seat;
+
+        g_variant_get (parameters, "(&s&o)", &id, &path);
+        seat = login1_service_get_seat (service, id);
+        if (seat)
+        {
+            service->priv->seats = g_list_remove (service->priv->seats, seat);
+            g_signal_emit (service, service_signals[SEAT_REMOVED], 0, seat);
+            g_object_unref (seat);
+        }
+    }
+}
+
+gboolean
+login1_service_connect (Login1Service *service)
 {
-    GDBusConnection *bus;
     GVariant *result;
-    gchar *session_path;
+    GVariantIter *seat_iter;
+    const gchar *id, *path;
     GError *error = NULL;
 
-    bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+    g_return_val_if_fail (service != NULL, FALSE);
+
+    if (service->priv->connected)
+        return TRUE;
+
+    service->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
     if (error)
         g_warning ("Failed to get system bus: %s", error->message);
     g_clear_error (&error);
-    if (!bus)
-        return NULL;
-    result = g_dbus_connection_call_sync (bus,
-                                          "org.freedesktop.login1",
-                                          "/org/freedesktop/login1",
-                                          "org.freedesktop.login1.Manager",
-                                          "GetSessionByPID",
-                                          g_variant_new ("(u)", getpid()),
-                                          G_VARIANT_TYPE ("(o)"),
+    if (!service->priv->connection)
+        return FALSE;
+
+    service->priv->signal_id = g_dbus_connection_signal_subscribe (service->priv->connection,
+                                                                   LOGIN1_SERVICE_NAME,
+                                                                   LOGIN1_MANAGER_INTERFACE_NAME,
+                                                                   NULL,
+                                                                   LOGIN1_OBJECT_NAME,
+                                                                   NULL,
+                                                                   G_DBUS_SIGNAL_FLAGS_NONE,
+                                                                   signal_cb,
+                                                                   g_object_ref (service),
+                                                                   g_object_unref);
+
+    result = g_dbus_connection_call_sync (service->priv->connection,
+                                          LOGIN1_SERVICE_NAME,
+                                          LOGIN1_OBJECT_NAME,
+                                          LOGIN1_MANAGER_INTERFACE_NAME,
+                                          "ListSeats",
+                                          g_variant_new ("()"),
+                                          G_VARIANT_TYPE ("(a(so))"),
                                           G_DBUS_CALL_FLAGS_NONE,
                                           -1,
                                           NULL,
                                           &error);
-    g_object_unref (bus);
-
     if (error)
-        g_warning ("Failed to open login1 session: %s", error->message);
+        g_warning ("Failed to get list of logind seats: %s", error->message);
     g_clear_error (&error);
     if (!result)
-        return NULL;
+        return FALSE;
 
-    g_variant_get (result, "(o)", &session_path);
+    g_variant_get (result, "(a(so))", &seat_iter);
+    while (g_variant_iter_loop (seat_iter, "(&s&o)", &id, &path))
+        add_seat (service, id, path);
+    g_variant_iter_free (seat_iter);
     g_variant_unref (result);
-    g_debug ("Got login1 session id: %s", session_path);
 
-    return session_path;
+    service->priv->connected = TRUE;
+
+    return TRUE;
+}
+
+gboolean
+login1_service_get_is_connected (Login1Service *service)
+{
+    g_return_val_if_fail (service != NULL, FALSE);
+    return service->priv->connected;
+}
+
+GList *
+login1_service_get_seats (Login1Service *service)
+{
+    g_return_val_if_fail (service != NULL, NULL);
+    return service->priv->seats;
+}
+
+Login1Seat *
+login1_service_get_seat (Login1Service *service, const gchar *id)
+{
+    GList *link;
+
+    g_return_val_if_fail (service != NULL, NULL);
+
+    for (link = service->priv->seats; link; link = link->next)
+    {
+        Login1Seat *seat = link->data;
+        if (strcmp (seat->priv->id, id) == 0)
+            return seat;
+    }
+
+    return NULL;
 }
 
 void
-login1_lock_session (const gchar *session_path)
+login1_service_lock_session (Login1Service *service, const gchar *session_id)
 {
-    GDBusConnection *bus;
     GError *error = NULL;
 
-    g_return_if_fail (session_path != NULL);
+    g_return_if_fail (service != NULL);
+    g_return_if_fail (session_id != NULL);
 
-    g_debug ("Locking login1 session %s", session_path);
+    g_debug ("Locking login1 session %s", session_id);
 
-    bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
-    if (error)
-        g_warning ("Failed to get system bus: %s", error->message);
-    g_clear_error (&error);
-    if (!bus)
-        return;
-
-    if (session_path)
+    if (session_id)
     {
         GVariant *result;
 
-        result = g_dbus_connection_call_sync (bus,
-                                              "org.freedesktop.login1",
-                                              session_path,
-                                              "org.freedesktop.login1.Session",
-                                              "Lock",
-                                              g_variant_new ("()"),
+        result = g_dbus_connection_call_sync (service->priv->connection,
+                                              LOGIN1_SERVICE_NAME,
+                                              LOGIN1_OBJECT_NAME,
+                                              LOGIN1_MANAGER_INTERFACE_NAME,
+                                              "LockSession",
+                                              g_variant_new ("(s)", session_id),
                                               G_VARIANT_TYPE ("()"),
                                               G_DBUS_CALL_FLAGS_NONE,
                                               -1,
@@ -134,36 +369,28 @@ login1_lock_session (const gchar *session_path)
         if (result)
             g_variant_unref (result);
     }
-    g_object_unref (bus);
 }
 
 void
-login1_unlock_session (const gchar *session_path)
+login1_service_unlock_session (Login1Service *service, const gchar *session_id)
 {
-    GDBusConnection *bus;
     GError *error = NULL;
 
-    g_return_if_fail (session_path != NULL);
+    g_return_if_fail (service != NULL);
+    g_return_if_fail (session_id != NULL);
 
-    g_debug ("Unlocking login1 session %s", session_path);
-
-    bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
-    if (error)
-        g_warning ("Failed to get system bus: %s", error->message);
-    g_clear_error (&error);
-    if (!bus)
-        return;
+    g_debug ("Unlocking login1 session %s", session_id);
 
-    if (session_path)
+    if (session_id)
     {
         GVariant *result;
 
-        result = g_dbus_connection_call_sync (bus,
-                                              "org.freedesktop.login1",
-                                              session_path,
-                                              "org.freedesktop.login1.Session",
-                                              "Unlock",
-                                              g_variant_new ("()"),
+        result = g_dbus_connection_call_sync (service->priv->connection,
+                                              LOGIN1_SERVICE_NAME,
+                                              LOGIN1_OBJECT_NAME,
+                                              LOGIN1_MANAGER_INTERFACE_NAME,
+                                              "UnlockSession",
+                                              g_variant_new ("(s)", session_id),
                                               G_VARIANT_TYPE ("()"),
                                               G_DBUS_CALL_FLAGS_NONE,
                                               -1,
@@ -175,36 +402,28 @@ login1_unlock_session (const gchar *session_path)
         if (result)
             g_variant_unref (result);
     }
-    g_object_unref (bus);
 }
 
 void
-login1_activate_session (const gchar *session_path)
+login1_service_activate_session (Login1Service *service, const gchar *session_id)
 {
-    GDBusConnection *bus;
     GError *error = NULL;
 
-    g_return_if_fail (session_path != NULL);
+    g_return_if_fail (service != NULL);
+    g_return_if_fail (session_id != NULL);
 
-    g_debug ("Activating login1 session %s", session_path);
+    g_debug ("Activating login1 session %s", session_id);
 
-    bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
-    if (error)
-        g_warning ("Failed to get system bus: %s", error->message);
-    g_clear_error (&error);
-    if (!bus)
-        return;
-
-    if (session_path)
+    if (session_id)
     {
         GVariant *result;
 
-        result = g_dbus_connection_call_sync (bus,
-                                              "org.freedesktop.login1",
-                                              session_path,
-                                              "org.freedesktop.login1.Session",
-                                              "Activate",
-                                              g_variant_new ("()"),
+        result = g_dbus_connection_call_sync (service->priv->connection,
+                                              LOGIN1_SERVICE_NAME,
+                                              LOGIN1_OBJECT_NAME,
+                                              LOGIN1_MANAGER_INTERFACE_NAME,
+                                              "ActivateSession",
+                                              g_variant_new ("(s)", session_id),
                                               G_VARIANT_TYPE ("()"),
                                               G_DBUS_CALL_FLAGS_NONE,
                                               -1,
@@ -216,5 +435,117 @@ login1_activate_session (const gchar *session_path)
         if (result)
             g_variant_unref (result);
     }
-    g_object_unref (bus);
+}
+
+static void
+login1_service_init (Login1Service *service)
+{
+    service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, LOGIN1_SERVICE_TYPE, Login1ServicePrivate);
+}
+
+static void
+login1_service_finalize (GObject *object)
+{
+    Login1Service *self = LOGIN1_SERVICE (object);
+
+    g_list_free_full (self->priv->seats, g_object_unref);
+    g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
+    g_clear_object (&self->priv->connection);
+
+    G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
+}
+
+static void
+login1_service_class_init (Login1ServiceClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = login1_service_finalize;
+
+    g_type_class_add_private (klass, sizeof (Login1ServicePrivate));
+
+    service_signals[SEAT_ADDED] =
+        g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
+    service_signals[SEAT_REMOVED] =
+        g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
+}
+
+const gchar *
+login1_seat_get_id (Login1Seat *seat)
+{
+    g_return_val_if_fail (seat != NULL, NULL);
+    return seat->priv->id;
+}
+
+gboolean
+login1_seat_get_can_graphical (Login1Seat *seat)
+{
+    g_return_val_if_fail (seat != NULL, FALSE);
+    return seat->priv->can_graphical;
+}
+
+gboolean
+login1_seat_get_can_multi_session (Login1Seat *seat)
+{
+    g_return_val_if_fail (seat != NULL, FALSE);
+    return seat->priv->can_multi_session;
+}
+
+static void
+login1_seat_init (Login1Seat *seat)
+{
+    seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, LOGIN1_SEAT_TYPE, Login1SeatPrivate);
+}
+
+static void
+login1_seat_finalize (GObject *object)
+{
+    Login1Seat *self = LOGIN1_SEAT (object);
+
+    g_free (self->priv->id);
+    g_free (self->priv->path);
+    g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
+    g_object_unref (self->priv->connection);
+
+    G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
+}
+
+static void
+login1_seat_class_init (Login1SeatClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = login1_seat_finalize;
+
+    g_type_class_add_private (klass, sizeof (Login1SeatPrivate));
+
+    seat_signals[CAN_GRAPHICAL_CHANGED] =
+        g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 0);
+
+    seat_signals[ACTIVE_SESSION_CHANGED] =
+        g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 1, G_TYPE_STRING);
 }