1 /* -*- Mode: C; indent-tabs-mode: nil; tab-width: 4 -*-
2 *
3 * Copyright (C) 2010-2011 Robert Ancell.
4 * Author: Robert Ancell <robert.ancell@canonical.com>
5 *
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
10 * license.
11 */
12
13 #include <string.h>
14 #include <gio/gio.h>
15
16 #include "login1.h"
17
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
22 enum {
23 SEAT_ADDED,
24 SEAT_REMOVED,
25 LAST_SERVICE_SIGNAL
26 };
27 static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 };
28
29 typedef struct
30 {
31 /* Connection to bus service is running on */
32 GDBusConnection *connection;
33
34 /* TRUE if have connected to service */
35 gboolean connected;
36
37 /* Seats the service is reporting */
38 GList *seats;
39
40 /* Handle to signal subscription */
41 guint signal_id;
42 } Login1ServicePrivate;
43
44 enum {
45 CAN_GRAPHICAL_CHANGED,
46 ACTIVE_SESSION_CHANGED,
47 LAST_SEAT_SIGNAL
48 };
49 static guint seat_signals[LAST_SEAT_SIGNAL] = { 0 };
50
51 typedef struct
52 {
53 /* Connection to bus seat is running on */
54 GDBusConnection *connection;
55
56 /* Seat Id */
57 gchar *id;
58
59 /* D-Bus path for this seat */
60 gchar *path;
61
62 /* Handle to signal subscription */
63 guint signal_id;
64
65 /* TRUE if can run a graphical display on this seat */
66 gboolean can_graphical;
67
68 /* TRUE if can do session switching */
69 gboolean can_multi_session;
70 } Login1SeatPrivate;
71
72 G_DEFINE_TYPE_WITH_PRIVATE (Login1Service, login1_service, G_TYPE_OBJECT)
73 G_DEFINE_TYPE_WITH_PRIVATE (Login1Seat, login1_seat, G_TYPE_OBJECT)
74
75 G_DEFINE_AUTOPTR_CLEANUP_FUNC (Login1Seat, g_object_unref)
76
77 static Login1Service *singleton = NULL;
78
79 Login1Service *
login1_service_get_instance(void)80 login1_service_get_instance (void)
81 {
82 if (!singleton)
83 singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
84 return singleton;
85 }
86
87 static void
update_property(Login1Seat * seat,const gchar * name,GVariant * value)88 update_property (Login1Seat *seat, const gchar *name, GVariant *value)
89 {
90 Login1SeatPrivate *priv = login1_seat_get_instance_private (seat);
91
92 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
93 {
94 priv->can_graphical = g_variant_get_boolean (value);
95 g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
96 }
97 else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE ("(so)")))
98 {
99 const gchar *login1_session_id;
100 g_variant_get (value, "(&so)", &login1_session_id, NULL);
101 g_signal_emit (seat, seat_signals[ACTIVE_SESSION_CHANGED], 0, login1_session_id);
102 }
103 }
104
105 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)106 seat_properties_changed_cb (GDBusConnection *connection,
107 const gchar *sender_name,
108 const gchar *object_path,
109 const gchar *interface_name,
110 const gchar *signal_name,
111 GVariant *parameters,
112 gpointer user_data)
113 {
114 Login1Seat *seat = user_data;
115 Login1SeatPrivate *priv = login1_seat_get_instance_private (seat);
116
117 g_autoptr(GVariantIter) iter = NULL;
118 g_autoptr(GVariantIter) invalidated_properties = NULL;
119 g_variant_get (parameters, "(sa{sv}as)", NULL, &iter, &invalidated_properties);
120
121 const gchar *name;
122 GVariant *value;
123 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
124 update_property (seat, name, value);
125
126 while (g_variant_iter_loop (invalidated_properties, "&s", &name))
127 {
128 g_autoptr(GError) error = NULL;
129 g_autoptr(GVariant) result = g_dbus_connection_call_sync (connection,
130 LOGIN1_SERVICE_NAME,
131 priv->path,
132 "org.freedesktop.DBus.Properties",
133 "Get",
134 g_variant_new ("(ss)", "org.freedesktop.login1.Seat", name),
135 G_VARIANT_TYPE ("(v)"),
136 G_DBUS_CALL_FLAGS_NONE,
137 -1,
138 NULL,
139 &error);
140 if (error)
141 g_warning ("Error updating seat property %s: %s", name, error->message);
142 if (result)
143 {
144 g_autoptr(GVariant) v = NULL;
145 g_variant_get (result, "(v)", &v);
146 update_property (seat, name, v);
147 }
148 }
149 }
150
151 static Login1Seat *
add_seat(Login1Service * service,const gchar * id,const gchar * path)152 add_seat (Login1Service *service, const gchar *id, const gchar *path)
153 {
154 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
155
156 Login1Seat *seat = g_object_new (LOGIN1_SEAT_TYPE, NULL);
157 Login1SeatPrivate *s_priv = login1_seat_get_instance_private (seat);
158
159 s_priv->connection = g_object_ref (priv->connection);
160 s_priv->id = g_strdup (id);
161 s_priv->path = g_strdup (path);
162
163 s_priv->signal_id = g_dbus_connection_signal_subscribe (s_priv->connection,
164 LOGIN1_SERVICE_NAME,
165 "org.freedesktop.DBus.Properties",
166 "PropertiesChanged",
167 path,
168 "org.freedesktop.login1.Seat",
169 G_DBUS_SIGNAL_FLAGS_NONE,
170 seat_properties_changed_cb,
171 g_object_ref (seat),
172 g_object_unref);
173
174 /* Get properties for this seat */
175 g_autoptr(GError) error = NULL;
176 g_autoptr(GVariant) result = g_dbus_connection_call_sync (s_priv->connection,
177 LOGIN1_SERVICE_NAME,
178 path,
179 "org.freedesktop.DBus.Properties",
180 "GetAll",
181 g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
182 G_VARIANT_TYPE ("(a{sv})"),
183 G_DBUS_CALL_FLAGS_NONE,
184 -1,
185 NULL,
186 &error);
187 if (error)
188 g_warning ("Failed to get seat properties: %s", error->message);
189 if (result)
190 {
191 g_autoptr(GVariantIter) properties = NULL;
192 g_variant_get (result, "(a{sv})", &properties);
193
194 const gchar *name;
195 GVariant *value;
196 while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
197 {
198 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
199 s_priv->can_graphical = g_variant_get_boolean (value);
200 else if (strcmp (name, "CanMultiSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
201 s_priv->can_multi_session = g_variant_get_boolean (value);
202 }
203 }
204
205 priv->seats = g_list_append (priv->seats, seat);
206
207 return seat;
208 }
209
210 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)211 signal_cb (GDBusConnection *connection,
212 const gchar *sender_name,
213 const gchar *object_path,
214 const gchar *interface_name,
215 const gchar *signal_name,
216 GVariant *parameters,
217 gpointer user_data)
218 {
219 Login1Service *service = user_data;
220 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
221
222 if (strcmp (signal_name, "SeatNew") == 0)
223 {
224 const gchar *id, *path;
225 g_variant_get (parameters, "(&s&o)", &id, &path);
226
227 Login1Seat *seat = login1_service_get_seat (service, id);
228 if (!seat)
229 {
230 seat = add_seat (service, id, path);
231 g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
232 }
233 }
234 else if (strcmp (signal_name, "SeatRemoved") == 0)
235 {
236 const gchar *id, *path;
237 g_variant_get (parameters, "(&s&o)", &id, &path);
238
239 g_autoptr(Login1Seat) seat = login1_service_get_seat (service, id);
240 if (seat)
241 {
242 priv->seats = g_list_remove (priv->seats, seat);
243 g_signal_emit (service, service_signals[SEAT_REMOVED], 0, seat);
244 }
245 }
246 }
247
248 gboolean
login1_service_connect(Login1Service * service)249 login1_service_connect (Login1Service *service)
250 {
251 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
252
253 g_return_val_if_fail (service != NULL, FALSE);
254
255 if (priv->connected)
256 return TRUE;
257
258 g_autoptr(GError) error = NULL;
259 priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
260 if (error)
261 g_warning ("Failed to get system bus: %s", error->message);
262 if (!priv->connection)
263 return FALSE;
264
265 priv->signal_id = g_dbus_connection_signal_subscribe (priv->connection,
266 LOGIN1_SERVICE_NAME,
267 LOGIN1_MANAGER_INTERFACE_NAME,
268 NULL,
269 LOGIN1_OBJECT_NAME,
270 NULL,
271 G_DBUS_SIGNAL_FLAGS_NONE,
272 signal_cb,
273 g_object_ref (service),
274 g_object_unref);
275
276 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->connection,
277 LOGIN1_SERVICE_NAME,
278 LOGIN1_OBJECT_NAME,
279 LOGIN1_MANAGER_INTERFACE_NAME,
280 "ListSeats",
281 g_variant_new ("()"),
282 G_VARIANT_TYPE ("(a(so))"),
283 G_DBUS_CALL_FLAGS_NONE,
284 -1,
285 NULL,
286 &error);
287 if (error)
288 g_warning ("Failed to get list of logind seats: %s", error->message);
289 if (!result)
290 return FALSE;
291
292 g_autoptr(GVariantIter) seat_iter = NULL;
293 g_variant_get (result, "(a(so))", &seat_iter);
294
295 const gchar *id, *path;
296 while (g_variant_iter_loop (seat_iter, "(&s&o)", &id, &path))
297 add_seat (service, id, path);
298
299 priv->connected = TRUE;
300
301 return TRUE;
302 }
303
304 gboolean
login1_service_get_is_connected(Login1Service * service)305 login1_service_get_is_connected (Login1Service *service)
306 {
307 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
308 g_return_val_if_fail (service != NULL, FALSE);
309 return priv->connected;
310 }
311
312 GList *
login1_service_get_seats(Login1Service * service)313 login1_service_get_seats (Login1Service *service)
314 {
315 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
316 g_return_val_if_fail (service != NULL, NULL);
317 return priv->seats;
318 }
319
320 Login1Seat *
login1_service_get_seat(Login1Service * service,const gchar * id)321 login1_service_get_seat (Login1Service *service, const gchar *id)
322 {
323 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
324
325 g_return_val_if_fail (service != NULL, NULL);
326
327 for (GList *link = priv->seats; link; link = link->next)
328 {
329 Login1Seat *seat = link->data;
330 Login1SeatPrivate *s_priv = login1_seat_get_instance_private (seat);
331
332 if (strcmp (s_priv->id, id) == 0)
333 return seat;
334 }
335
336 return NULL;
337 }
338
339 void
login1_service_lock_session(Login1Service * service,const gchar * session_id)340 login1_service_lock_session (Login1Service *service, const gchar *session_id)
341 {
342 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
343
344 g_return_if_fail (service != NULL);
345 g_return_if_fail (session_id != NULL);
346
347 g_debug ("Locking login1 session %s", session_id);
348
349 if (!session_id)
350 return;
351
352 g_autoptr(GError) error = NULL;
353 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->connection,
354 LOGIN1_SERVICE_NAME,
355 LOGIN1_OBJECT_NAME,
356 LOGIN1_MANAGER_INTERFACE_NAME,
357 "LockSession",
358 g_variant_new ("(s)", session_id),
359 G_VARIANT_TYPE ("()"),
360 G_DBUS_CALL_FLAGS_NONE,
361 -1,
362 NULL,
363 &error);
364 if (error)
365 g_warning ("Error locking login1 session: %s", error->message);
366 }
367
368 void
login1_service_unlock_session(Login1Service * service,const gchar * session_id)369 login1_service_unlock_session (Login1Service *service, const gchar *session_id)
370 {
371 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
372
373 g_return_if_fail (service != NULL);
374 g_return_if_fail (session_id != NULL);
375
376 g_debug ("Unlocking login1 session %s", session_id);
377
378 if (!session_id)
379 return;
380
381 g_autoptr(GError) error = NULL;
382 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->connection,
383 LOGIN1_SERVICE_NAME,
384 LOGIN1_OBJECT_NAME,
385 LOGIN1_MANAGER_INTERFACE_NAME,
386 "UnlockSession",
387 g_variant_new ("(s)", session_id),
388 G_VARIANT_TYPE ("()"),
389 G_DBUS_CALL_FLAGS_NONE,
390 -1,
391 NULL,
392 &error);
393 if (error)
394 g_warning ("Error unlocking login1 session: %s", error->message);
395 }
396
397 void
login1_service_activate_session(Login1Service * service,const gchar * session_id)398 login1_service_activate_session (Login1Service *service, const gchar *session_id)
399 {
400 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
401
402 g_return_if_fail (service != NULL);
403 g_return_if_fail (session_id != NULL);
404
405 g_debug ("Activating login1 session %s", session_id);
406
407 if (!session_id)
408 return;
409
410 g_autoptr(GError) error = NULL;
411 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->connection,
412 LOGIN1_SERVICE_NAME,
413 LOGIN1_OBJECT_NAME,
414 LOGIN1_MANAGER_INTERFACE_NAME,
415 "ActivateSession",
416 g_variant_new ("(s)", session_id),
417 G_VARIANT_TYPE ("()"),
418 G_DBUS_CALL_FLAGS_NONE,
419 -1,
420 NULL,
421 &error);
422 if (error)
423 g_warning ("Error activating login1 session: %s", error->message);
424 }
425
426 void
login1_service_terminate_session(Login1Service * service,const gchar * session_id)427 login1_service_terminate_session (Login1Service *service, const gchar *session_id)
428 {
429 Login1ServicePrivate *priv = login1_service_get_instance_private (service);
430
431 g_return_if_fail (service != NULL);
432 g_return_if_fail (session_id != NULL);
433
434 g_debug ("Terminating login1 session %s", session_id);
435
436 if (!session_id)
437 return;
438
439 g_autoptr(GError) error = NULL;
440 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->connection,
441 LOGIN1_SERVICE_NAME,
442 LOGIN1_OBJECT_NAME,
443 LOGIN1_MANAGER_INTERFACE_NAME,
444 "TerminateSession",
445 g_variant_new ("(s)", session_id),
446 G_VARIANT_TYPE ("()"),
447 G_DBUS_CALL_FLAGS_NONE,
448 -1,
449 NULL,
450 &error);
451 if (error)
452 g_warning ("Error terminating login1 session: %s", error->message);
453 }
454
455 static void
login1_service_init(Login1Service * service)456 login1_service_init (Login1Service *service)
457 {
458 }
459
460 static void
login1_service_finalize(GObject * object)461 login1_service_finalize (GObject *object)
462 {
463 Login1Service *self = LOGIN1_SERVICE (object);
464 Login1ServicePrivate *priv = login1_service_get_instance_private (self);
465
466 g_list_free_full (priv->seats, g_object_unref);
467 g_dbus_connection_signal_unsubscribe (priv->connection, priv->signal_id);
468 g_clear_object (&priv->connection);
469
470 G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
471 }
472
473 static void
login1_service_class_init(Login1ServiceClass * klass)474 login1_service_class_init (Login1ServiceClass *klass)
475 {
476 GObjectClass *object_class = G_OBJECT_CLASS (klass);
477
478 object_class->finalize = login1_service_finalize;
479
480 service_signals[SEAT_ADDED] =
481 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
482 G_TYPE_FROM_CLASS (klass),
483 G_SIGNAL_RUN_LAST,
484 G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
485 NULL, NULL,
486 NULL,
487 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
488 service_signals[SEAT_REMOVED] =
489 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED,
490 G_TYPE_FROM_CLASS (klass),
491 G_SIGNAL_RUN_LAST,
492 G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
493 NULL, NULL,
494 NULL,
495 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
496 }
497
498 const gchar *
login1_seat_get_id(Login1Seat * seat)499 login1_seat_get_id (Login1Seat *seat)
500 {
501 Login1SeatPrivate *priv = login1_seat_get_instance_private (seat);
502 g_return_val_if_fail (seat != NULL, NULL);
503 return priv->id;
504 }
505
506 gboolean
login1_seat_get_can_graphical(Login1Seat * seat)507 login1_seat_get_can_graphical (Login1Seat *seat)
508 {
509 Login1SeatPrivate *priv = login1_seat_get_instance_private (seat);
510 g_return_val_if_fail (seat != NULL, FALSE);
511 return priv->can_graphical;
512 }
513
514 gboolean
login1_seat_get_can_multi_session(Login1Seat * seat)515 login1_seat_get_can_multi_session (Login1Seat *seat)
516 {
517 Login1SeatPrivate *priv = login1_seat_get_instance_private (seat);
518 g_return_val_if_fail (seat != NULL, FALSE);
519 return priv->can_multi_session;
520 }
521
522 static void
login1_seat_init(Login1Seat * seat)523 login1_seat_init (Login1Seat *seat)
524 {
525 }
526
527 static void
login1_seat_finalize(GObject * object)528 login1_seat_finalize (GObject *object)
529 {
530 Login1Seat *self = LOGIN1_SEAT (object);
531 Login1SeatPrivate *priv = login1_seat_get_instance_private (self);
532
533 g_clear_pointer (&priv->id, g_free);
534 g_clear_pointer (&priv->path, g_free);
535 g_dbus_connection_signal_unsubscribe (priv->connection, priv->signal_id);
536 g_clear_object (&priv->connection);
537
538 G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
539 }
540
541 static void
login1_seat_class_init(Login1SeatClass * klass)542 login1_seat_class_init (Login1SeatClass *klass)
543 {
544 GObjectClass *object_class = G_OBJECT_CLASS (klass);
545
546 object_class->finalize = login1_seat_finalize;
547
548 seat_signals[CAN_GRAPHICAL_CHANGED] =
549 g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
550 G_TYPE_FROM_CLASS (klass),
551 G_SIGNAL_RUN_LAST,
552 G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
553 NULL, NULL,
554 NULL,
555 G_TYPE_NONE, 0);
556
557 seat_signals[ACTIVE_SESSION_CHANGED] =
558 g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
559 G_TYPE_FROM_CLASS (klass),
560 G_SIGNAL_RUN_LAST,
561 G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
562 NULL, NULL,
563 NULL,
564 G_TYPE_NONE, 1, G_TYPE_STRING);
565 }
566