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