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