]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/login1.c
Load all users only when really needed
[sojka/lightdm.git] / src / login1.c
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 struct Login1ServicePrivate
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 };
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 struct Login1SeatPrivate
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 };
71
72 G_DEFINE_TYPE (Login1Service, login1_service, G_TYPE_OBJECT);
73 G_DEFINE_TYPE (Login1Seat, login1_seat, G_TYPE_OBJECT);
74
75 static Login1Service *singleton = NULL;
76
77 Login1Service *
78 login1_service_get_instance (void)
79 {
80     if (!singleton)
81         singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
82     return singleton;
83 }
84
85 static void
86 update_property (Login1Seat *seat, const gchar *name, GVariant *value)
87 {
88     if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
89     {
90         seat->priv->can_graphical = g_variant_get_boolean (value);
91         g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
92     }
93     else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE ("(so)")))
94     {
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);
98     }
99 }
100
101 static void
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,
108                             gpointer user_data)
109 {
110     Login1Seat *seat = user_data;
111     GVariantIter *iter;
112     GVariantIter *invalidated_properties;
113     const gchar *name;
114     GVariant *value;
115
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))
121     {
122         GVariant *result;
123         GError *error = NULL;
124
125         result = g_dbus_connection_call_sync (connection,
126                                               LOGIN1_SERVICE_NAME,
127                                               seat->priv->path,
128                                               "org.freedesktop.DBus.Properties",
129                                               "Get",
130                                               g_variant_new ("(ss)", "org.freedesktop.login1.Seat", name),
131                                               G_VARIANT_TYPE ("(v)"),
132                                               G_DBUS_CALL_FLAGS_NONE,
133                                               -1,
134                                               NULL,
135                                               &error);
136         if (error)
137             g_warning ("Error updating seat property %s: %s", name, error->message);
138         g_clear_error (&error);
139         if (result)
140         {
141             g_variant_get (result, "(v)", &value);
142             update_property (seat, name, value);
143             g_variant_unref (value);
144             g_variant_unref (result);
145         }
146     }
147     g_variant_iter_free (invalidated_properties);
148 }
149
150 static Login1Seat *
151 add_seat (Login1Service *service, const gchar *id, const gchar *path)
152 {
153     Login1Seat *seat;
154     GVariant *result;
155     GError *error = NULL;
156
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);
161
162     seat->priv->signal_id = g_dbus_connection_signal_subscribe (seat->priv->connection,
163                                                                 LOGIN1_SERVICE_NAME,
164                                                                 "org.freedesktop.DBus.Properties",
165                                                                 "PropertiesChanged",
166                                                                 path,
167                                                                 "org.freedesktop.login1.Seat",
168                                                                 G_DBUS_SIGNAL_FLAGS_NONE,
169                                                                 seat_properties_changed_cb,
170                                                                 g_object_ref (seat),
171                                                                 g_object_unref);
172
173     /* Get properties for this seat */
174     result = g_dbus_connection_call_sync (seat->priv->connection,
175                                           LOGIN1_SERVICE_NAME,
176                                           path,
177                                           "org.freedesktop.DBus.Properties",
178                                           "GetAll",
179                                           g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
180                                           G_VARIANT_TYPE ("(a{sv})"),
181                                           G_DBUS_CALL_FLAGS_NONE,
182                                           -1,
183                                           NULL,
184                                           &error);
185     if (error)
186         g_warning ("Failed to get seat properties: %s", error->message);
187     g_clear_error (&error);
188     if (result)
189     {
190         GVariantIter *properties;
191         const gchar *name;
192         GVariant *value;
193
194         g_variant_get (result, "(a{sv})", &properties);
195         while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
196         {
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);
201         }
202         g_variant_iter_free (properties);
203         g_variant_unref (result);
204     }
205
206     service->priv->seats = g_list_append (service->priv->seats, seat);
207
208     return seat;
209 }
210
211 static void
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,
218            gpointer user_data)
219 {
220     Login1Service *service = user_data;
221
222     if (strcmp (signal_name, "SeatNew") == 0)
223     {
224         const gchar *id, *path;
225         Login1Seat *seat;
226
227         g_variant_get (parameters, "(&s&o)", &id, &path);
228         seat = login1_service_get_seat (service, id);
229         if (!seat)
230         {
231             seat = add_seat (service, id, path);
232             g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
233         }
234     }
235     else if (strcmp (signal_name, "SeatRemoved") == 0)
236     {
237         const gchar *id, *path;
238         Login1Seat *seat;
239
240         g_variant_get (parameters, "(&s&o)", &id, &path);
241         seat = login1_service_get_seat (service, id);
242         if (seat)
243         {
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);
247         }
248     }
249 }
250
251 gboolean
252 login1_service_connect (Login1Service *service)
253 {
254     GVariant *result;
255     GVariantIter *seat_iter;
256     const gchar *id, *path;
257     GError *error = NULL;
258
259     g_return_val_if_fail (service != NULL, FALSE);
260
261     if (service->priv->connected)
262         return TRUE;
263
264     service->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
265     if (error)
266         g_warning ("Failed to get system bus: %s", error->message);
267     g_clear_error (&error);
268     if (!service->priv->connection)
269         return FALSE;
270
271     service->priv->signal_id = g_dbus_connection_signal_subscribe (service->priv->connection,
272                                                                    LOGIN1_SERVICE_NAME,
273                                                                    LOGIN1_MANAGER_INTERFACE_NAME,
274                                                                    NULL,
275                                                                    LOGIN1_OBJECT_NAME,
276                                                                    NULL,
277                                                                    G_DBUS_SIGNAL_FLAGS_NONE,
278                                                                    signal_cb,
279                                                                    g_object_ref (service),
280                                                                    g_object_unref);
281
282     result = g_dbus_connection_call_sync (service->priv->connection,
283                                           LOGIN1_SERVICE_NAME,
284                                           LOGIN1_OBJECT_NAME,
285                                           LOGIN1_MANAGER_INTERFACE_NAME,
286                                           "ListSeats",
287                                           g_variant_new ("()"),
288                                           G_VARIANT_TYPE ("(a(so))"),
289                                           G_DBUS_CALL_FLAGS_NONE,
290                                           -1,
291                                           NULL,
292                                           &error);
293     if (error)
294         g_warning ("Failed to get list of logind seats: %s", error->message);
295     g_clear_error (&error);
296     if (!result)
297         return FALSE;
298
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);
304
305     service->priv->connected = TRUE;
306
307     return TRUE;
308 }
309
310 gboolean
311 login1_service_get_is_connected (Login1Service *service)
312 {
313     g_return_val_if_fail (service != NULL, FALSE);
314     return service->priv->connected;
315 }
316
317 GList *
318 login1_service_get_seats (Login1Service *service)
319 {
320     g_return_val_if_fail (service != NULL, NULL);
321     return service->priv->seats;
322 }
323
324 Login1Seat *
325 login1_service_get_seat (Login1Service *service, const gchar *id)
326 {
327     GList *link;
328
329     g_return_val_if_fail (service != NULL, NULL);
330
331     for (link = service->priv->seats; link; link = link->next)
332     {
333         Login1Seat *seat = link->data;
334         if (strcmp (seat->priv->id, id) == 0)
335             return seat;
336     }
337
338     return NULL;
339 }
340
341 void
342 login1_service_lock_session (Login1Service *service, const gchar *session_id)
343 {
344     GError *error = NULL;
345
346     g_return_if_fail (service != NULL);
347     g_return_if_fail (session_id != NULL);
348
349     g_debug ("Locking login1 session %s", session_id);
350
351     if (session_id)
352     {
353         GVariant *result;
354
355         result = g_dbus_connection_call_sync (service->priv->connection,
356                                               LOGIN1_SERVICE_NAME,
357                                               LOGIN1_OBJECT_NAME,
358                                               LOGIN1_MANAGER_INTERFACE_NAME,
359                                               "LockSession",
360                                               g_variant_new ("(s)", session_id),
361                                               G_VARIANT_TYPE ("()"),
362                                               G_DBUS_CALL_FLAGS_NONE,
363                                               -1,
364                                               NULL,
365                                               &error);
366         if (error)
367             g_warning ("Error locking login1 session: %s", error->message);
368         g_clear_error (&error);
369         if (result)
370             g_variant_unref (result);
371     }
372 }
373
374 void
375 login1_service_unlock_session (Login1Service *service, const gchar *session_id)
376 {
377     GError *error = NULL;
378
379     g_return_if_fail (service != NULL);
380     g_return_if_fail (session_id != NULL);
381
382     g_debug ("Unlocking login1 session %s", session_id);
383
384     if (session_id)
385     {
386         GVariant *result;
387
388         result = g_dbus_connection_call_sync (service->priv->connection,
389                                               LOGIN1_SERVICE_NAME,
390                                               LOGIN1_OBJECT_NAME,
391                                               LOGIN1_MANAGER_INTERFACE_NAME,
392                                               "UnlockSession",
393                                               g_variant_new ("(s)", session_id),
394                                               G_VARIANT_TYPE ("()"),
395                                               G_DBUS_CALL_FLAGS_NONE,
396                                               -1,
397                                               NULL,
398                                               &error);
399         if (error)
400             g_warning ("Error unlocking login1 session: %s", error->message);
401         g_clear_error (&error);
402         if (result)
403             g_variant_unref (result);
404     }
405 }
406
407 void
408 login1_service_activate_session (Login1Service *service, const gchar *session_id)
409 {
410     GError *error = NULL;
411
412     g_return_if_fail (service != NULL);
413     g_return_if_fail (session_id != NULL);
414
415     g_debug ("Activating login1 session %s", session_id);
416
417     if (session_id)
418     {
419         GVariant *result;
420
421         result = g_dbus_connection_call_sync (service->priv->connection,
422                                               LOGIN1_SERVICE_NAME,
423                                               LOGIN1_OBJECT_NAME,
424                                               LOGIN1_MANAGER_INTERFACE_NAME,
425                                               "ActivateSession",
426                                               g_variant_new ("(s)", session_id),
427                                               G_VARIANT_TYPE ("()"),
428                                               G_DBUS_CALL_FLAGS_NONE,
429                                               -1,
430                                               NULL,
431                                               &error);
432         if (error)
433             g_warning ("Error activating login1 session: %s", error->message);
434         g_clear_error (&error);
435         if (result)
436             g_variant_unref (result);
437     }
438 }
439
440 static void
441 login1_service_init (Login1Service *service)
442 {
443     service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, LOGIN1_SERVICE_TYPE, Login1ServicePrivate);
444 }
445
446 static void
447 login1_service_finalize (GObject *object)
448 {
449     Login1Service *self = LOGIN1_SERVICE (object);
450
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);
454
455     G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
456 }
457
458 static void
459 login1_service_class_init (Login1ServiceClass *klass)
460 {
461     GObjectClass *object_class = G_OBJECT_CLASS (klass);
462
463     object_class->finalize = login1_service_finalize;
464
465     g_type_class_add_private (klass, sizeof (Login1ServicePrivate));
466
467     service_signals[SEAT_ADDED] =
468         g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
469                       G_TYPE_FROM_CLASS (klass),
470                       G_SIGNAL_RUN_LAST,
471                       G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
472                       NULL, NULL,
473                       NULL,
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),
478                       G_SIGNAL_RUN_LAST,
479                       G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
480                       NULL, NULL,
481                       NULL,
482                       G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
483 }
484
485 const gchar *
486 login1_seat_get_id (Login1Seat *seat)
487 {
488     g_return_val_if_fail (seat != NULL, NULL);
489     return seat->priv->id;
490 }
491
492 gboolean
493 login1_seat_get_can_graphical (Login1Seat *seat)
494 {
495     g_return_val_if_fail (seat != NULL, FALSE);
496     return seat->priv->can_graphical;
497 }
498
499 gboolean
500 login1_seat_get_can_multi_session (Login1Seat *seat)
501 {
502     g_return_val_if_fail (seat != NULL, FALSE);
503     return seat->priv->can_multi_session;
504 }
505
506 static void
507 login1_seat_init (Login1Seat *seat)
508 {
509     seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, LOGIN1_SEAT_TYPE, Login1SeatPrivate);
510 }
511
512 static void
513 login1_seat_finalize (GObject *object)
514 {
515     Login1Seat *self = LOGIN1_SEAT (object);
516
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);
521
522     G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
523 }
524
525 static void
526 login1_seat_class_init (Login1SeatClass *klass)
527 {
528     GObjectClass *object_class = G_OBJECT_CLASS (klass);
529
530     object_class->finalize = login1_seat_finalize;
531
532     g_type_class_add_private (klass, sizeof (Login1SeatPrivate));
533
534     seat_signals[CAN_GRAPHICAL_CHANGED] =
535         g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
536                       G_TYPE_FROM_CLASS (klass),
537                       G_SIGNAL_RUN_LAST,
538                       G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
539                       NULL, NULL,
540                       NULL,
541                       G_TYPE_NONE, 0);
542
543     seat_signals[ACTIVE_SESSION_CHANGED] =
544         g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
545                       G_TYPE_FROM_CLASS (klass),
546                       G_SIGNAL_RUN_LAST,
547                       G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
548                       NULL, NULL,
549                       NULL,
550                       G_TYPE_NONE, 1, G_TYPE_STRING);
551 }