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