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