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