1 /* -*- Mode: C; indent-tabs-mode: nil; tab-width: 4 -*-
3 * Copyright (C) 2010-2011 Robert Ancell.
4 * Author: Robert Ancell <robert.ancell@canonical.com>
6 * This program is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
18 #define LOGIN1_SERVICE_NAME "org.freedesktop.login1"
19 #define LOGIN1_OBJECT_NAME "/org/freedesktop/login1"
20 #define LOGIN1_MANAGER_INTERFACE_NAME "org.freedesktop.login1.Manager"
27 static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 };
29 struct Login1ServicePrivate
31 /* Connection to bus service is running on */
32 GDBusConnection *connection;
34 /* TRUE if have connected to service */
37 /* Seats the service is reporting */
40 /* Handle to signal subscription */
45 CAN_GRAPHICAL_CHANGED,
46 ACTIVE_SESSION_CHANGED,
49 static guint seat_signals[LAST_SEAT_SIGNAL] = { 0 };
51 struct Login1SeatPrivate
53 /* Connection to bus seat is running on */
54 GDBusConnection *connection;
59 /* D-Bus path for this seat */
62 /* Handle to signal subscription */
65 /* TRUE if can run a graphical display on this seat */
66 gboolean can_graphical;
68 /* TRUE if can do session switching */
69 gboolean can_multi_session;
72 G_DEFINE_TYPE (Login1Service, login1_service, G_TYPE_OBJECT);
73 G_DEFINE_TYPE (Login1Seat, login1_seat, G_TYPE_OBJECT);
75 static Login1Service *singleton = NULL;
78 login1_service_get_instance (void)
81 singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
86 update_property (Login1Seat *seat, const gchar *name, GVariant *value)
88 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
90 seat->priv->can_graphical = g_variant_get_boolean (value);
91 g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
93 else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE ("(so)")))
95 const gchar *login1_session_id;
96 g_variant_get (value, "(&so)", &login1_session_id, NULL);
97 g_signal_emit (seat, seat_signals[ACTIVE_SESSION_CHANGED], 0, login1_session_id);
102 seat_properties_changed_cb (GDBusConnection *connection,
103 const gchar *sender_name,
104 const gchar *object_path,
105 const gchar *interface_name,
106 const gchar *signal_name,
107 GVariant *parameters,
110 Login1Seat *seat = user_data;
112 GVariantIter *invalidated_properties;
116 g_variant_get (parameters, "(sa{sv}as)", NULL, &iter, &invalidated_properties);
117 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
118 update_property (seat, name, value);
119 g_variant_iter_free (iter);
120 while (g_variant_iter_loop (invalidated_properties, "&s", &name))
123 GError *error = NULL;
125 result = g_dbus_connection_call_sync (connection,
128 "org.freedesktop.DBus.Properties",
130 g_variant_new ("(ss)", "org.freedesktop.login1.Seat", name),
131 G_VARIANT_TYPE ("(v)"),
132 G_DBUS_CALL_FLAGS_NONE,
137 g_warning ("Error updating seat property %s: %s", name, error->message);
138 g_clear_error (&error);
141 g_variant_get (result, "(v)", &value);
142 update_property (seat, name, value);
143 g_variant_unref (value);
144 g_variant_unref (result);
147 g_variant_iter_free (invalidated_properties);
151 add_seat (Login1Service *service, const gchar *id, const gchar *path)
155 GError *error = NULL;
157 seat = g_object_new (LOGIN1_SEAT_TYPE, NULL);
158 seat->priv->connection = g_object_ref (service->priv->connection);
159 seat->priv->id = g_strdup (id);
160 seat->priv->path = g_strdup (path);
162 seat->priv->signal_id = g_dbus_connection_signal_subscribe (seat->priv->connection,
164 "org.freedesktop.DBus.Properties",
167 "org.freedesktop.login1.Seat",
168 G_DBUS_SIGNAL_FLAGS_NONE,
169 seat_properties_changed_cb,
173 /* Get properties for this seat */
174 result = g_dbus_connection_call_sync (seat->priv->connection,
177 "org.freedesktop.DBus.Properties",
179 g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
180 G_VARIANT_TYPE ("(a{sv})"),
181 G_DBUS_CALL_FLAGS_NONE,
186 g_warning ("Failed to get seat properties: %s", error->message);
187 g_clear_error (&error);
190 GVariantIter *properties;
194 g_variant_get (result, "(a{sv})", &properties);
195 while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
197 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
198 seat->priv->can_graphical = g_variant_get_boolean (value);
199 else if (strcmp (name, "CanMultiSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
200 seat->priv->can_multi_session = g_variant_get_boolean (value);
202 g_variant_iter_free (properties);
203 g_variant_unref (result);
206 service->priv->seats = g_list_append (service->priv->seats, seat);
212 signal_cb (GDBusConnection *connection,
213 const gchar *sender_name,
214 const gchar *object_path,
215 const gchar *interface_name,
216 const gchar *signal_name,
217 GVariant *parameters,
220 Login1Service *service = user_data;
222 if (strcmp (signal_name, "SeatNew") == 0)
224 const gchar *id, *path;
227 g_variant_get (parameters, "(&s&o)", &id, &path);
228 seat = login1_service_get_seat (service, id);
231 seat = add_seat (service, id, path);
232 g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
235 else if (strcmp (signal_name, "SeatRemoved") == 0)
237 const gchar *id, *path;
240 g_variant_get (parameters, "(&s&o)", &id, &path);
241 seat = login1_service_get_seat (service, id);
244 service->priv->seats = g_list_remove (service->priv->seats, seat);
245 g_signal_emit (service, service_signals[SEAT_REMOVED], 0, seat);
246 g_object_unref (seat);
252 login1_service_connect (Login1Service *service)
255 GVariantIter *seat_iter;
256 const gchar *id, *path;
257 GError *error = NULL;
259 g_return_val_if_fail (service != NULL, FALSE);
261 if (service->priv->connected)
264 service->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
266 g_warning ("Failed to get system bus: %s", error->message);
267 g_clear_error (&error);
268 if (!service->priv->connection)
271 service->priv->signal_id = g_dbus_connection_signal_subscribe (service->priv->connection,
273 LOGIN1_MANAGER_INTERFACE_NAME,
277 G_DBUS_SIGNAL_FLAGS_NONE,
279 g_object_ref (service),
282 result = g_dbus_connection_call_sync (service->priv->connection,
285 LOGIN1_MANAGER_INTERFACE_NAME,
287 g_variant_new ("()"),
288 G_VARIANT_TYPE ("(a(so))"),
289 G_DBUS_CALL_FLAGS_NONE,
294 g_warning ("Failed to get list of logind seats: %s", error->message);
295 g_clear_error (&error);
299 g_variant_get (result, "(a(so))", &seat_iter);
300 while (g_variant_iter_loop (seat_iter, "(&s&o)", &id, &path))
301 add_seat (service, id, path);
302 g_variant_iter_free (seat_iter);
303 g_variant_unref (result);
305 service->priv->connected = TRUE;
311 login1_service_get_is_connected (Login1Service *service)
313 g_return_val_if_fail (service != NULL, FALSE);
314 return service->priv->connected;
318 login1_service_get_seats (Login1Service *service)
320 g_return_val_if_fail (service != NULL, NULL);
321 return service->priv->seats;
325 login1_service_get_seat (Login1Service *service, const gchar *id)
329 g_return_val_if_fail (service != NULL, NULL);
331 for (link = service->priv->seats; link; link = link->next)
333 Login1Seat *seat = link->data;
334 if (strcmp (seat->priv->id, id) == 0)
342 login1_service_lock_session (Login1Service *service, const gchar *session_id)
344 GError *error = NULL;
346 g_return_if_fail (service != NULL);
347 g_return_if_fail (session_id != NULL);
349 g_debug ("Locking login1 session %s", session_id);
355 result = g_dbus_connection_call_sync (service->priv->connection,
358 LOGIN1_MANAGER_INTERFACE_NAME,
360 g_variant_new ("(s)", session_id),
361 G_VARIANT_TYPE ("()"),
362 G_DBUS_CALL_FLAGS_NONE,
367 g_warning ("Error locking login1 session: %s", error->message);
368 g_clear_error (&error);
370 g_variant_unref (result);
375 login1_service_unlock_session (Login1Service *service, const gchar *session_id)
377 GError *error = NULL;
379 g_return_if_fail (service != NULL);
380 g_return_if_fail (session_id != NULL);
382 g_debug ("Unlocking login1 session %s", session_id);
388 result = g_dbus_connection_call_sync (service->priv->connection,
391 LOGIN1_MANAGER_INTERFACE_NAME,
393 g_variant_new ("(s)", session_id),
394 G_VARIANT_TYPE ("()"),
395 G_DBUS_CALL_FLAGS_NONE,
400 g_warning ("Error unlocking login1 session: %s", error->message);
401 g_clear_error (&error);
403 g_variant_unref (result);
408 login1_service_activate_session (Login1Service *service, const gchar *session_id)
410 GError *error = NULL;
412 g_return_if_fail (service != NULL);
413 g_return_if_fail (session_id != NULL);
415 g_debug ("Activating login1 session %s", session_id);
421 result = g_dbus_connection_call_sync (service->priv->connection,
424 LOGIN1_MANAGER_INTERFACE_NAME,
426 g_variant_new ("(s)", session_id),
427 G_VARIANT_TYPE ("()"),
428 G_DBUS_CALL_FLAGS_NONE,
433 g_warning ("Error activating login1 session: %s", error->message);
434 g_clear_error (&error);
436 g_variant_unref (result);
441 login1_service_init (Login1Service *service)
443 service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, LOGIN1_SERVICE_TYPE, Login1ServicePrivate);
447 login1_service_finalize (GObject *object)
449 Login1Service *self = LOGIN1_SERVICE (object);
451 g_list_free_full (self->priv->seats, g_object_unref);
452 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
453 g_clear_object (&self->priv->connection);
455 G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
459 login1_service_class_init (Login1ServiceClass *klass)
461 GObjectClass *object_class = G_OBJECT_CLASS (klass);
463 object_class->finalize = login1_service_finalize;
465 g_type_class_add_private (klass, sizeof (Login1ServicePrivate));
467 service_signals[SEAT_ADDED] =
468 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
469 G_TYPE_FROM_CLASS (klass),
471 G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
474 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
475 service_signals[SEAT_REMOVED] =
476 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED,
477 G_TYPE_FROM_CLASS (klass),
479 G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
482 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
486 login1_seat_get_id (Login1Seat *seat)
488 g_return_val_if_fail (seat != NULL, NULL);
489 return seat->priv->id;
493 login1_seat_get_can_graphical (Login1Seat *seat)
495 g_return_val_if_fail (seat != NULL, FALSE);
496 return seat->priv->can_graphical;
500 login1_seat_get_can_multi_session (Login1Seat *seat)
502 g_return_val_if_fail (seat != NULL, FALSE);
503 return seat->priv->can_multi_session;
507 login1_seat_init (Login1Seat *seat)
509 seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, LOGIN1_SEAT_TYPE, Login1SeatPrivate);
513 login1_seat_finalize (GObject *object)
515 Login1Seat *self = LOGIN1_SEAT (object);
517 g_free (self->priv->id);
518 g_free (self->priv->path);
519 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
520 g_object_unref (self->priv->connection);
522 G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
526 login1_seat_class_init (Login1SeatClass *klass)
528 GObjectClass *object_class = G_OBJECT_CLASS (klass);
530 object_class->finalize = login1_seat_finalize;
532 g_type_class_add_private (klass, sizeof (Login1SeatPrivate));
534 seat_signals[CAN_GRAPHICAL_CHANGED] =
535 g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
536 G_TYPE_FROM_CLASS (klass),
538 G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
543 seat_signals[ACTIVE_SESSION_CHANGED] =
544 g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
545 G_TYPE_FROM_CLASS (klass),
547 G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
550 G_TYPE_NONE, 1, G_TYPE_STRING);