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