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