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"
21 #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
22 #define DBUS_PROPERTIES_GET_METHOD "Get"
23 #define LOGIN1_SEAT_INTERFACE "org.freedesktop.login1.Seat"
30 static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 };
32 struct Login1ServicePrivate
34 /* Connection to bus service is running on */
35 GDBusConnection *connection;
37 /* TRUE if have connected to service */
40 /* Seats the service is reporting */
43 /* Handle to signal subscription */
48 CAN_GRAPHICAL_CHANGED,
49 ACTIVE_SESSION_CHANGED,
52 static guint seat_signals[LAST_SEAT_SIGNAL] = { 0, 0 };
54 struct Login1SeatPrivate
56 /* Connection to bus seat is running on */
57 GDBusConnection *connection;
62 /* D-Bus path for this seat */
65 /* Handle to signal subscription */
68 /* TRUE if can run a graphical display on this seat */
69 gboolean can_graphical;
71 /* TRUE if can do session switching */
72 gboolean can_multi_session;
75 G_DEFINE_TYPE (Login1Service, login1_service, G_TYPE_OBJECT);
76 G_DEFINE_TYPE (Login1Seat, login1_seat, G_TYPE_OBJECT);
78 static Login1Service *singleton = NULL;
81 login1_service_get_instance (void)
84 singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
89 get_property_from_dbus (GDBusConnection *connection,
91 const gchar *property_name)
96 result = g_dbus_connection_call_sync (connection,
99 DBUS_PROPERTIES_INTERFACE,
100 DBUS_PROPERTIES_GET_METHOD,
101 g_variant_new ("(ss)", LOGIN1_SEAT_INTERFACE, property_name),
102 G_VARIANT_TYPE ("(v)"),
103 G_DBUS_CALL_FLAGS_NONE,
108 g_warning ("Error updating %s: %s", property_name, error->message);
109 g_clear_error (&error);
115 update_property (Login1Seat *seat, const gchar *name, GVariant *value)
117 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
119 seat->priv->can_graphical = g_variant_get_boolean (value);
120 g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
122 else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_ARRAY))
126 // value should be of type (so)
127 value2 = g_variant_get_child_value (value, 0);
130 const gchar *login1_session_id;
132 login1_session_id = g_variant_get_string (value2, NULL);
134 if (login1_session_id)
136 g_signal_emit (seat, seat_signals[ACTIVE_SESSION_CHANGED], 0, login1_session_id);
139 g_variant_unref (value2);
145 seat_properties_changed_cb (GDBusConnection *connection,
146 const gchar *sender_name,
147 const gchar *object_path,
148 const gchar *interface_name,
149 const gchar *signal_name,
150 GVariant *parameters,
153 Login1Seat *seat = user_data;
154 GVariantIter *changed_properties;
155 GVariantIter *invalidated_properties;
156 const gchar *property_name;
157 GVariant *property_value;
159 g_variant_get (parameters, "(sa{sv}as)", NULL, &changed_properties, &invalidated_properties);
161 while (g_variant_iter_loop (changed_properties, "{&sv}", &property_name, &property_value))
163 update_property (seat, property_name, property_value);
165 g_variant_iter_free (changed_properties);
167 while (g_variant_iter_loop (invalidated_properties, "&s", &property_name))
171 result = get_property_from_dbus (connection, seat, property_name);
174 g_variant_get (result, "(v)", &property_value);
175 update_property (seat, property_name, property_value);
176 g_variant_unref (property_value);
178 g_variant_unref (result);
181 g_variant_iter_free (invalidated_properties);
185 add_seat (Login1Service *service, const gchar *id, const gchar *path)
189 GError *error = NULL;
191 seat = g_object_new (LOGIN1_SEAT_TYPE, NULL);
192 seat->priv->connection = g_object_ref (service->priv->connection);
193 seat->priv->id = g_strdup (id);
194 seat->priv->path = g_strdup (path);
196 seat->priv->signal_id = g_dbus_connection_signal_subscribe (seat->priv->connection,
198 "org.freedesktop.DBus.Properties",
201 "org.freedesktop.login1.Seat",
202 G_DBUS_SIGNAL_FLAGS_NONE,
203 seat_properties_changed_cb,
207 /* Get properties for this seat */
208 result = g_dbus_connection_call_sync (seat->priv->connection,
211 "org.freedesktop.DBus.Properties",
213 g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
214 G_VARIANT_TYPE ("(a{sv})"),
215 G_DBUS_CALL_FLAGS_NONE,
220 g_warning ("Failed to get seat properties: %s", error->message);
221 g_clear_error (&error);
224 GVariantIter *properties;
228 g_variant_get (result, "(a{sv})", &properties);
229 while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
231 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
232 seat->priv->can_graphical = g_variant_get_boolean (value);
233 else if (strcmp (name, "CanMultiSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
234 seat->priv->can_multi_session = g_variant_get_boolean (value);
236 g_variant_iter_free (properties);
237 g_variant_unref (result);
240 service->priv->seats = g_list_append (service->priv->seats, seat);
246 signal_cb (GDBusConnection *connection,
247 const gchar *sender_name,
248 const gchar *object_path,
249 const gchar *interface_name,
250 const gchar *signal_name,
251 GVariant *parameters,
254 Login1Service *service = user_data;
256 if (strcmp (signal_name, "SeatNew") == 0)
258 const gchar *id, *path;
261 g_variant_get (parameters, "(&s&o)", &id, &path);
262 seat = login1_service_get_seat (service, id);
265 seat = add_seat (service, id, path);
266 g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
269 else if (strcmp (signal_name, "SeatRemoved") == 0)
271 const gchar *id, *path;
274 g_variant_get (parameters, "(&s&o)", &id, &path);
275 seat = login1_service_get_seat (service, id);
278 service->priv->seats = g_list_remove (service->priv->seats, seat);
279 g_signal_emit (service, service_signals[SEAT_REMOVED], 0, seat);
280 g_object_unref (seat);
286 login1_service_connect (Login1Service *service)
289 GVariantIter *seat_iter;
290 const gchar *id, *path;
291 GError *error = NULL;
293 g_return_val_if_fail (service != NULL, FALSE);
295 if (service->priv->connected)
298 service->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
300 g_warning ("Failed to get system bus: %s", error->message);
301 g_clear_error (&error);
302 if (!service->priv->connection)
305 service->priv->signal_id = g_dbus_connection_signal_subscribe (service->priv->connection,
307 LOGIN1_MANAGER_INTERFACE_NAME,
311 G_DBUS_SIGNAL_FLAGS_NONE,
313 g_object_ref (service),
316 result = g_dbus_connection_call_sync (service->priv->connection,
319 LOGIN1_MANAGER_INTERFACE_NAME,
321 g_variant_new ("()"),
322 G_VARIANT_TYPE ("(a(so))"),
323 G_DBUS_CALL_FLAGS_NONE,
328 g_warning ("Failed to get list of logind seats: %s", error->message);
329 g_clear_error (&error);
333 g_variant_get (result, "(a(so))", &seat_iter);
334 while (g_variant_iter_loop (seat_iter, "(&s&o)", &id, &path))
335 add_seat (service, id, path);
336 g_variant_iter_free (seat_iter);
337 g_variant_unref (result);
339 service->priv->connected = TRUE;
345 login1_service_get_is_connected (Login1Service *service)
347 g_return_val_if_fail (service != NULL, FALSE);
348 return service->priv->connected;
352 login1_service_get_seats (Login1Service *service)
354 g_return_val_if_fail (service != NULL, NULL);
355 return service->priv->seats;
359 login1_service_get_seat (Login1Service *service, const gchar *id)
363 g_return_val_if_fail (service != NULL, NULL);
365 for (link = service->priv->seats; link; link = link->next)
367 Login1Seat *seat = link->data;
368 if (strcmp (seat->priv->id, id) == 0)
376 login1_service_lock_session (Login1Service *service, const gchar *session_id)
378 GError *error = NULL;
380 g_return_if_fail (service != NULL);
381 g_return_if_fail (session_id != NULL);
383 g_debug ("Locking login1 session %s", session_id);
389 result = g_dbus_connection_call_sync (service->priv->connection,
392 LOGIN1_MANAGER_INTERFACE_NAME,
394 g_variant_new ("(s)", session_id),
395 G_VARIANT_TYPE ("()"),
396 G_DBUS_CALL_FLAGS_NONE,
401 g_warning ("Error locking login1 session: %s", error->message);
402 g_clear_error (&error);
404 g_variant_unref (result);
409 login1_service_unlock_session (Login1Service *service, const gchar *session_id)
411 GError *error = NULL;
413 g_return_if_fail (service != NULL);
414 g_return_if_fail (session_id != NULL);
416 g_debug ("Unlocking login1 session %s", session_id);
422 result = g_dbus_connection_call_sync (service->priv->connection,
425 LOGIN1_MANAGER_INTERFACE_NAME,
427 g_variant_new ("(s)", session_id),
428 G_VARIANT_TYPE ("()"),
429 G_DBUS_CALL_FLAGS_NONE,
434 g_warning ("Error unlocking login1 session: %s", error->message);
435 g_clear_error (&error);
437 g_variant_unref (result);
442 login1_service_activate_session (Login1Service *service, const gchar *session_id)
444 GError *error = NULL;
446 g_return_if_fail (service != NULL);
447 g_return_if_fail (session_id != NULL);
449 g_debug ("Activating login1 session %s", session_id);
455 result = g_dbus_connection_call_sync (service->priv->connection,
458 LOGIN1_MANAGER_INTERFACE_NAME,
460 g_variant_new ("(s)", session_id),
461 G_VARIANT_TYPE ("()"),
462 G_DBUS_CALL_FLAGS_NONE,
467 g_warning ("Error activating login1 session: %s", error->message);
468 g_clear_error (&error);
470 g_variant_unref (result);
475 login1_service_init (Login1Service *service)
477 service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, LOGIN1_SERVICE_TYPE, Login1ServicePrivate);
481 login1_service_finalize (GObject *object)
483 Login1Service *self = LOGIN1_SERVICE (object);
485 g_list_free_full (self->priv->seats, g_object_unref);
486 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
487 g_object_unref (self->priv->connection);
489 G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
493 login1_service_class_init (Login1ServiceClass *klass)
495 GObjectClass *object_class = G_OBJECT_CLASS (klass);
497 object_class->finalize = login1_service_finalize;
499 g_type_class_add_private (klass, sizeof (Login1ServicePrivate));
501 service_signals[SEAT_ADDED] =
502 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
503 G_TYPE_FROM_CLASS (klass),
505 G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
508 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
509 service_signals[SEAT_REMOVED] =
510 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED,
511 G_TYPE_FROM_CLASS (klass),
513 G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
516 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
520 login1_seat_get_id (Login1Seat *seat)
522 g_return_val_if_fail (seat != NULL, NULL);
523 return seat->priv->id;
527 login1_seat_get_can_graphical (Login1Seat *seat)
529 g_return_val_if_fail (seat != NULL, FALSE);
530 return seat->priv->can_graphical;
534 login1_seat_get_can_multi_session (Login1Seat *seat)
536 g_return_val_if_fail (seat != NULL, FALSE);
537 return seat->priv->can_multi_session;
541 login1_seat_init (Login1Seat *seat)
543 seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, LOGIN1_SEAT_TYPE, Login1SeatPrivate);
547 login1_seat_finalize (GObject *object)
549 Login1Seat *self = LOGIN1_SEAT (object);
551 g_free (self->priv->id);
552 g_free (self->priv->path);
553 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
554 g_object_unref (self->priv->connection);
556 G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
560 login1_seat_class_init (Login1SeatClass *klass)
562 GObjectClass *object_class = G_OBJECT_CLASS (klass);
564 object_class->finalize = login1_seat_finalize;
566 g_type_class_add_private (klass, sizeof (Login1SeatPrivate));
568 seat_signals[CAN_GRAPHICAL_CHANGED] =
569 g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
570 G_TYPE_FROM_CLASS (klass),
572 G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
577 seat_signals[ACTIVE_SESSION_CHANGED] =
578 g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
579 G_TYPE_FROM_CLASS (klass),
581 G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
584 G_TYPE_NONE, 1, G_TYPE_STRING);