1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2008 Jon McCann <jmccann@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 
28 #include <glib.h>
29 #include <glib-object.h>
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 #include <gio/gunixfdlist.h>
33 
34 #include "gsm-system.h"
35 #include "gsm-consolekit.h"
36 
37 #define CK_NAME      "org.freedesktop.ConsoleKit"
38 
39 #define CK_MANAGER_PATH         "/org/freedesktop/ConsoleKit/Manager"
40 #define CK_MANAGER_INTERFACE    CK_NAME ".Manager"
41 #define CK_SEAT_INTERFACE       CK_NAME ".Seat"
42 #define CK_SESSION_INTERFACE    CK_NAME ".Session"
43 
44 
45 struct _GsmConsolekitPrivate
46 {
47         GDBusProxy      *ck_proxy;
48         GDBusProxy      *ck_session_proxy;
49 
50         char            *session_id;
51         gchar           *session_path;
52 
53         const gchar     *inhibit_locks;
54         gint             inhibit_fd;
55 
56         gboolean         is_active;
57 
58         gint             delay_inhibit_fd;
59         gboolean         prepare_for_shutdown_expected;
60 };
61 
62 enum {
63         PROP_0,
64         PROP_ACTIVE
65 };
66 
67 static void gsm_consolekit_system_init (GsmSystemInterface *iface);
68 
G_DEFINE_TYPE_WITH_CODE(GsmConsolekit,gsm_consolekit,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GSM_TYPE_SYSTEM,gsm_consolekit_system_init))69 G_DEFINE_TYPE_WITH_CODE (GsmConsolekit, gsm_consolekit, G_TYPE_OBJECT,
70                          G_IMPLEMENT_INTERFACE (GSM_TYPE_SYSTEM,
71                                                 gsm_consolekit_system_init))
72 
73 static void
74 drop_system_inhibitor (GsmConsolekit *manager)
75 {
76         if (manager->priv->inhibit_fd != -1) {
77                 g_debug ("GsmConsolekit: Dropping system inhibitor fd %d", manager->priv->inhibit_fd);
78                 close (manager->priv->inhibit_fd);
79                 manager->priv->inhibit_fd = -1;
80         }
81 }
82 
83 static void
drop_delay_inhibitor(GsmConsolekit * manager)84 drop_delay_inhibitor (GsmConsolekit *manager)
85 {
86         if (manager->priv->delay_inhibit_fd != -1) {
87                 g_debug ("GsmConsolekit: Dropping delay inhibitor");
88                 close (manager->priv->delay_inhibit_fd);
89                 manager->priv->delay_inhibit_fd = -1;
90         }
91 }
92 
93 static void
gsm_consolekit_finalize(GObject * object)94 gsm_consolekit_finalize (GObject *object)
95 {
96         GsmConsolekit *consolekit = GSM_CONSOLEKIT (object);
97 
98         g_clear_object (&consolekit->priv->ck_proxy);
99         g_clear_object (&consolekit->priv->ck_session_proxy);
100         free (consolekit->priv->session_id);
101         g_free (consolekit->priv->session_path);
102 
103         drop_system_inhibitor (consolekit);
104         drop_delay_inhibitor (consolekit);
105 
106         G_OBJECT_CLASS (gsm_consolekit_parent_class)->finalize (object);
107 }
108 
109 static void
gsm_consolekit_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)110 gsm_consolekit_set_property (GObject      *object,
111                              guint         prop_id,
112                              const GValue *value,
113                              GParamSpec   *pspec)
114 {
115         GsmConsolekit *self = GSM_CONSOLEKIT (object);
116 
117         switch (prop_id) {
118         case PROP_ACTIVE:
119                 self->priv->is_active = g_value_get_boolean (value);
120                 break;
121         default:
122                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123         }
124 }
125 
126 static void
gsm_consolekit_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)127 gsm_consolekit_get_property (GObject    *object,
128                              guint       prop_id,
129                              GValue     *value,
130                              GParamSpec *pspec)
131 {
132         GsmConsolekit *self = GSM_CONSOLEKIT (object);
133 
134         switch (prop_id) {
135         case PROP_ACTIVE:
136                 g_value_set_boolean (value, self->priv->is_active);
137                 break;
138         default:
139                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140                 break;
141         }
142 }
143 
144 static void
gsm_consolekit_class_init(GsmConsolekitClass * manager_class)145 gsm_consolekit_class_init (GsmConsolekitClass *manager_class)
146 {
147         GObjectClass *object_class;
148 
149         object_class = G_OBJECT_CLASS (manager_class);
150 
151         object_class->get_property = gsm_consolekit_get_property;
152         object_class->set_property = gsm_consolekit_set_property;
153         object_class->finalize = gsm_consolekit_finalize;
154 
155         g_object_class_override_property (object_class, PROP_ACTIVE, "active");
156 
157         g_type_class_add_private (manager_class, sizeof (GsmConsolekitPrivate));
158 }
159 
160 static void ck_session_proxy_signal_cb (GDBusProxy  *proxy,
161                                         const gchar *sender_name,
162                                         const gchar *signal_name,
163                                         GVariant    *parameters,
164                                         gpointer     user_data);
165 
166 static void ck_proxy_signal_cb (GDBusProxy  *proxy,
167                                 const gchar *sender_name,
168                                 const gchar *signal_name,
169                                 GVariant    *parameters,
170                                 gpointer     user_data);
171 
172 static void
ck_pid_get_session(GsmConsolekit * manager,pid_t pid,gchar ** session_id)173 ck_pid_get_session (GsmConsolekit *manager,
174                     pid_t          pid,
175                     gchar        **session_id)
176 {
177         GVariant *res;
178 
179         *session_id = NULL;
180 
181         if (pid < 0) {
182                 g_warning ("Calling GetSessionForUnixProcess failed."
183                            "Invalid pid.");
184                 return;
185         }
186 
187         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
188                                       "GetSessionForUnixProcess",
189                                       g_variant_new ("(u)", pid),
190                                       0,
191                                       -1,
192                                       NULL,
193                                       NULL);
194         if (!res) {
195                 g_warning ("Calling GetSessionForUnixProcess failed."
196                            "Check that ConsoleKit is properly installed.");
197                 return;
198         }
199 
200         g_variant_get (res, "(o)", session_id);
201         g_variant_unref (res);
202 }
203 
204 static void
gsm_consolekit_init(GsmConsolekit * manager)205 gsm_consolekit_init (GsmConsolekit *manager)
206 {
207         GError *error = NULL;
208         GDBusConnection *bus;
209         GVariant *res;
210 
211         manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
212                                                      GSM_TYPE_CONSOLEKIT,
213                                                      GsmConsolekitPrivate);
214 
215         manager->priv->inhibit_fd = -1;
216         manager->priv->delay_inhibit_fd = -1;
217 
218         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
219         if (bus == NULL)
220                 g_error ("Failed to connect to system bus: %s",
221                          error->message);
222         manager->priv->ck_proxy =
223                 g_dbus_proxy_new_sync (bus,
224                                        0,
225                                        NULL,
226                                        CK_NAME,
227                                        CK_MANAGER_PATH,
228                                        CK_MANAGER_INTERFACE,
229                                        NULL,
230                                        &error);
231         if (manager->priv->ck_proxy == NULL) {
232                 g_warning ("Failed to connect to consolekit: %s",
233                            error->message);
234                 g_clear_error (&error);
235         }
236 
237         g_signal_connect (manager->priv->ck_proxy, "g-signal",
238                           G_CALLBACK (ck_proxy_signal_cb), manager);
239 
240         ck_pid_get_session (manager, getpid (), &manager->priv->session_id);
241 
242         if (manager->priv->session_id == NULL) {
243                 g_warning ("Could not get session id for session. Check that ConsoleKit is "
244                            "properly installed.");
245                 return;
246         }
247 
248         /* in ConsoleKit, the session id is the session path */
249         manager->priv->session_path = g_strdup (manager->priv->session_id);
250 
251         manager->priv->ck_session_proxy =
252                 g_dbus_proxy_new_sync (bus,
253                                        0,
254                                        NULL,
255                                        CK_NAME,
256                                        manager->priv->session_path,
257                                        CK_SESSION_INTERFACE,
258                                        NULL,
259                                        &error);
260         if (manager->priv->ck_proxy == NULL) {
261                 g_warning ("Failed to connect to consolekit session: %s",
262                            error->message);
263                 g_clear_error (&error);
264         }
265 
266         g_signal_connect (manager->priv->ck_session_proxy, "g-signal",
267                           G_CALLBACK (ck_session_proxy_signal_cb), manager);
268 
269         g_object_unref (bus);
270 }
271 
272 static void
emit_restart_complete(GsmConsolekit * manager,GError * error)273 emit_restart_complete (GsmConsolekit *manager,
274                        GError     *error)
275 {
276         GError *call_error;
277 
278         call_error = NULL;
279 
280         if (error != NULL) {
281                 call_error = g_error_new_literal (GSM_SYSTEM_ERROR,
282                                                   GSM_SYSTEM_ERROR_RESTARTING,
283                                                   error->message);
284         }
285 
286         g_signal_emit_by_name (G_OBJECT (manager),
287                                "request_completed", call_error);
288 
289         if (call_error != NULL) {
290                 g_error_free (call_error);
291         }
292 }
293 
294 static void
emit_stop_complete(GsmConsolekit * manager,GError * error)295 emit_stop_complete (GsmConsolekit *manager,
296                     GError     *error)
297 {
298         GError *call_error;
299 
300         call_error = NULL;
301 
302         if (error != NULL) {
303                 call_error = g_error_new_literal (GSM_SYSTEM_ERROR,
304                                                   GSM_SYSTEM_ERROR_STOPPING,
305                                                   error->message);
306         }
307 
308         g_signal_emit_by_name (G_OBJECT (manager),
309                                "request_completed", call_error);
310 
311         if (call_error != NULL) {
312                 g_error_free (call_error);
313         }
314 }
315 
316 static void
restart_done(GObject * source,GAsyncResult * result,gpointer user_data)317 restart_done (GObject      *source,
318               GAsyncResult *result,
319               gpointer      user_data)
320 {
321         GDBusProxy *proxy = G_DBUS_PROXY (source);
322         GsmConsolekit *manager = user_data;
323         GError *error = NULL;
324         GVariant *res;
325 
326         res = g_dbus_proxy_call_finish (proxy, result, &error);
327 
328         if (!res) {
329                 g_warning ("Unable to restart system: %s", error->message);
330                 emit_restart_complete (manager, error);
331                 g_error_free (error);
332         } else {
333                 emit_restart_complete (manager, NULL);
334                 g_variant_unref (res);
335         }
336 }
337 
338 static void
gsm_consolekit_attempt_restart(GsmSystem * system)339 gsm_consolekit_attempt_restart (GsmSystem *system)
340 {
341         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
342 
343         /* Use Restart instead of Reboot because it will work on
344          * both CK and CK2 */
345         g_dbus_proxy_call (manager->priv->ck_proxy,
346                            "Restart",
347                            g_variant_new ("()"),
348                            0,
349                            G_MAXINT,
350                            NULL,
351                            restart_done,
352                            manager);
353 }
354 
355 static void
stop_done(GObject * source,GAsyncResult * result,gpointer user_data)356 stop_done (GObject      *source,
357            GAsyncResult *result,
358            gpointer      user_data)
359 {
360         GDBusProxy *proxy = G_DBUS_PROXY (source);
361         GsmConsolekit *manager = user_data;
362         GError *error = NULL;
363         GVariant *res;
364 
365         res = g_dbus_proxy_call_finish (proxy, result, &error);
366 
367         if (!res) {
368                 g_warning ("Unable to stop system: %s", error->message);
369                 emit_stop_complete (manager, error);
370                 g_error_free (error);
371         } else {
372                 emit_stop_complete (manager, NULL);
373                 g_variant_unref (res);
374         }
375 }
376 
377 static void
gsm_consolekit_attempt_stop(GsmSystem * system)378 gsm_consolekit_attempt_stop (GsmSystem *system)
379 {
380         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
381 
382         /* Use Stop insetad of PowerOff because it will work with
383          * Ck and CK2. */
384         g_dbus_proxy_call (manager->priv->ck_proxy,
385                            "Stop",
386                            g_variant_new ("()"),
387                            0,
388                            G_MAXINT,
389                            NULL,
390                            stop_done,
391                            manager);
392 }
393 
394 static void
gsm_consolekit_set_session_idle(GsmSystem * system,gboolean is_idle)395 gsm_consolekit_set_session_idle (GsmSystem *system,
396                               gboolean   is_idle)
397 {
398         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
399 
400         g_debug ("Updating consolekit idle status: %d", is_idle);
401         g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
402                                 "SetIdleHint",
403                                 g_variant_new ("(b)", is_idle),
404                                 0,
405                                 G_MAXINT,
406                                 NULL, NULL);
407 }
408 
409 static void
ck_session_get_seat(GsmConsolekit * manager,gchar ** seat)410 ck_session_get_seat (GsmConsolekit *manager,
411                      gchar        **seat)
412 {
413         GVariant *res;
414 
415         *seat = NULL;
416 
417         res = g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
418                                       "GetSeatId",
419                                       g_variant_new ("()"),
420                                       0,
421                                       -1,
422                                       NULL, NULL);
423         if (!res) {
424                 g_warning ("GsmConsoleKit: Calling GetSeatId failed.");
425                 return;
426         }
427 
428         g_variant_get (res, "(o)", seat);
429         g_variant_unref (res);
430 }
431 
432 /* returns -1 on failure
433  *          0 seat is multi-session
434  *          1 seat is not multi-session
435  */
436 static gint
ck_seat_can_multi_session(GsmConsolekit * manager,const gchar * seat)437 ck_seat_can_multi_session (GsmConsolekit *manager,
438                            const gchar   *seat)
439 {
440         GDBusConnection *bus;
441         GVariant *res;
442         gboolean  can_activate;
443 
444 
445         bus = g_dbus_proxy_get_connection (manager->priv->ck_proxy);
446         res = g_dbus_connection_call_sync (bus,
447                                            CK_NAME,
448                                            seat,
449                                            CK_SEAT_INTERFACE,
450                                            "CanActivateSessions",
451                                            g_variant_new ("()"),
452                                            G_VARIANT_TYPE_BOOLEAN,
453                                            0,
454                                            -1,
455                                            NULL, NULL);
456         if (!res) {
457                 g_warning ("GsmConsoleKit: Calling GetSeatId failed.");
458                 return -1;
459         }
460 
461         g_variant_get (res, "(b)", &can_activate);
462         g_variant_unref (res);
463 
464         return can_activate == TRUE ? 1 : 0;
465 }
466 
467 static gboolean
gsm_consolekit_can_switch_user(GsmSystem * system)468 gsm_consolekit_can_switch_user (GsmSystem *system)
469 {
470         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
471         gchar *seat;
472         gint ret;
473 
474         ck_session_get_seat (manager, &seat);
475         ret = ck_seat_can_multi_session (manager, seat);
476         free (seat);
477 
478         return ret > 0;
479 }
480 
481 static gboolean
gsm_consolekit_can_restart(GsmSystem * system)482 gsm_consolekit_can_restart (GsmSystem *system)
483 {
484         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
485         GVariant *res;
486         gboolean can_restart;
487 
488         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
489                                       "CanRestart",
490                                       g_variant_new ("()"),
491                                       0,
492                                       G_MAXINT,
493                                       NULL,
494                                       NULL);
495         if (!res) {
496                 g_warning ("Calling CanRestart failed. Check that ConsoleKit is "
497                            "properly installed.");
498                 return FALSE;
499         }
500 
501         g_variant_get (res, "(b)", &can_restart);
502         g_variant_unref (res);
503 
504         return can_restart;
505 }
506 
507 static gboolean
gsm_consolekit_can_stop(GsmSystem * system)508 gsm_consolekit_can_stop (GsmSystem *system)
509 {
510         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
511         GVariant *res;
512         gboolean can_stop;
513 
514         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
515                                       "CanStop",
516                                       g_variant_new ("()"),
517                                       0,
518                                       G_MAXINT,
519                                       NULL,
520                                       NULL);
521         if (!res) {
522                 g_warning ("Calling CanStop failed. Check that ConsoleKit is "
523                            "properly installed.");
524                 return FALSE;
525         }
526 
527         g_variant_get (res, "(b)", &can_stop);
528         g_variant_unref (res);
529 
530         return can_stop;
531 }
532 
533 /* returns -1 on failure, 0 on success */
534 static gint
ck_session_get_class(GsmConsolekit * manager,gchar ** session_class)535 ck_session_get_class (GsmConsolekit *manager,
536                       gchar        **session_class)
537 {
538         GVariant *res;
539 
540         *session_class = NULL;
541 
542         res = g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
543                                       "GetSessionClass",
544                                       g_variant_new ("()"),
545                                       0,
546                                       -1,
547                                       NULL, NULL);
548         if (!res) {
549                 g_warning ("GsmConsoleKit: Calling GetSessionClass failed.");
550                 return -1;
551         }
552 
553         g_variant_get (res, "(s)", session_class);
554         g_variant_unref (res);
555 
556         return 0;
557 }
558 
559 static gboolean
gsm_consolekit_is_login_session(GsmSystem * system)560 gsm_consolekit_is_login_session (GsmSystem *system)
561 {
562         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
563         int res;
564         gboolean ret;
565         gchar *session_class = NULL;
566 
567         ret = FALSE;
568 
569         if (manager->priv->session_id == NULL) {
570                 return ret;
571         }
572 
573         res = ck_session_get_class (manager, &session_class);
574         if (res < 0) {
575                 g_warning ("Could not get session class: %s", strerror (-res));
576                 return FALSE;
577         }
578         ret = (g_strcmp0 (session_class, "greeter") == 0);
579         g_free (session_class);
580 
581         return ret;
582 }
583 
584 static gboolean
gsm_consolekit_can_suspend(GsmSystem * system)585 gsm_consolekit_can_suspend (GsmSystem *system)
586 {
587         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
588         gchar *rv;
589         GVariant *res;
590         gboolean can_suspend;
591 
592         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
593                                       "CanSuspend",
594                                       NULL,
595                                       0,
596                                       G_MAXINT,
597                                       NULL,
598                                       NULL);
599         if (!res) {
600                 g_warning ("Calling CanSuspend failed. Check that ConsoleKit is "
601                            "properly installed.");
602                 return FALSE;
603         }
604 
605         g_variant_get (res, "(s)", &rv);
606         g_variant_unref (res);
607 
608         can_suspend = g_strcmp0 (rv, "yes") == 0 ||
609                       g_strcmp0 (rv, "challenge") == 0;
610 
611         g_free (rv);
612 
613         return can_suspend;
614 }
615 
616 static gboolean
gsm_consolekit_can_hibernate(GsmSystem * system)617 gsm_consolekit_can_hibernate (GsmSystem *system)
618 {
619         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
620         gchar *rv;
621         GVariant *res;
622         gboolean can_hibernate;
623 
624         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
625                                       "CanHibernate",
626                                       NULL,
627                                       0,
628                                       G_MAXINT,
629                                       NULL,
630                                       NULL);
631         if (!res) {
632                 g_warning ("Calling CanHibernate failed. Check that ConsoleKit is "
633                            "properly installed.");
634                 return FALSE;
635         }
636 
637         g_variant_get (res, "(s)", &rv);
638         g_variant_unref (res);
639 
640         can_hibernate = g_strcmp0 (rv, "yes") == 0 ||
641                         g_strcmp0 (rv, "challenge") == 0;
642 
643         g_free (rv);
644 
645         return can_hibernate;
646 }
647 
648 static void
suspend_done(GObject * source,GAsyncResult * result,gpointer user_data)649 suspend_done (GObject      *source,
650               GAsyncResult *result,
651               gpointer      user_data)
652 {
653         GDBusProxy *proxy = G_DBUS_PROXY (source);
654         GError *error = NULL;
655         GVariant *res;
656 
657         res = g_dbus_proxy_call_finish (proxy, result, &error);
658 
659         if (!res) {
660                 g_warning ("Unable to suspend system: %s", error->message);
661                 g_error_free (error);
662         } else {
663                 g_variant_unref (res);
664         }
665 }
666 
667 static void
hibernate_done(GObject * source,GAsyncResult * result,gpointer user_data)668 hibernate_done (GObject      *source,
669                 GAsyncResult *result,
670                 gpointer      user_data)
671 {
672         GDBusProxy *proxy = G_DBUS_PROXY (source);
673         GError *error = NULL;
674         GVariant *res;
675 
676         res = g_dbus_proxy_call_finish (proxy, result, &error);
677 
678         if (!res) {
679                 g_warning ("Unable to hibernate system: %s", error->message);
680                 g_error_free (error);
681         } else {
682                 g_variant_unref (res);
683         }
684 }
685 
686 static void
gsm_consolekit_suspend(GsmSystem * system)687 gsm_consolekit_suspend (GsmSystem *system)
688 {
689         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
690 
691         g_dbus_proxy_call (manager->priv->ck_proxy,
692                            "Suspend",
693                            g_variant_new ("(b)", TRUE),
694                            0,
695                            G_MAXINT,
696                            NULL,
697                            suspend_done,
698                            manager);
699 }
700 
701 static void
gsm_consolekit_hibernate(GsmSystem * system)702 gsm_consolekit_hibernate (GsmSystem *system)
703 {
704         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
705 
706         g_dbus_proxy_call (manager->priv->ck_proxy,
707                            "Hibernate",
708                            g_variant_new ("(b)", TRUE),
709                            0,
710                            G_MAXINT,
711                            NULL,
712                            hibernate_done,
713                            manager);
714 }
715 
716 static void
inhibit_done(GObject * source,GAsyncResult * result,gpointer user_data)717 inhibit_done (GObject      *source,
718               GAsyncResult *result,
719               gpointer      user_data)
720 {
721         GDBusProxy *proxy = G_DBUS_PROXY (source);
722         GsmConsolekit *manager = GSM_CONSOLEKIT (user_data);
723         GError *error = NULL;
724         GVariant *res;
725         GUnixFDList *fd_list = NULL;
726         gint idx;
727 
728         /* Drop any previous inhibit before recording the new one */
729         drop_system_inhibitor (manager);
730 
731         res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
732 
733         if (!res) {
734                 g_warning ("Unable to inhibit system: %s", error->message);
735                 g_error_free (error);
736         } else {
737                 g_variant_get (res, "(h)", &idx);
738                 manager->priv->inhibit_fd = g_unix_fd_list_get (fd_list, idx, &error);
739                 if (manager->priv->inhibit_fd == -1) {
740                         g_warning ("Failed to receive system inhibitor fd: %s", error->message);
741                         g_error_free (error);
742                 }
743                 g_debug ("System inhibitor fd is %d", manager->priv->inhibit_fd);
744                 g_object_unref (fd_list);
745                 g_variant_unref (res);
746         }
747 
748         /* Handle a race condition, where locks got unset during dbus call */
749         if (manager->priv->inhibit_locks == NULL) {
750                 drop_system_inhibitor (manager);
751         }
752 }
753 
754 static void
gsm_consolekit_set_inhibitors(GsmSystem * system,GsmInhibitorFlag flags)755 gsm_consolekit_set_inhibitors (GsmSystem        *system,
756                                GsmInhibitorFlag  flags)
757 {
758         GsmConsolekit *manager = GSM_CONSOLEKIT (system);
759         const gchar *locks = NULL;
760         gboolean inhibit_logout;
761         gboolean inhibit_suspend;
762 
763         inhibit_logout = (flags & GSM_INHIBITOR_FLAG_LOGOUT) != 0;
764         inhibit_suspend = (flags & GSM_INHIBITOR_FLAG_SUSPEND) != 0;
765 
766         if (inhibit_logout && inhibit_suspend) {
767                 locks = "sleep:shutdown";
768         } else if (inhibit_logout) {
769                 locks = "shutdown";
770         } else if (inhibit_suspend) {
771                 locks = "sleep";
772         }
773         manager->priv->inhibit_locks = locks;
774 
775         if (locks != NULL) {
776                 g_debug ("Adding system inhibitor on %s", locks);
777                 g_dbus_proxy_call_with_unix_fd_list (manager->priv->ck_proxy,
778                                                      "Inhibit",
779                                                      g_variant_new ("(ssss)",
780                                                                     locks,
781                                                                     g_get_user_name (),
782                                                                     "user session inhibited",
783                                                                     "block"),
784                                                      0,
785                                                      G_MAXINT,
786                                                      NULL,
787                                                      NULL,
788                                                      inhibit_done,
789                                                      manager);
790         } else {
791                 drop_system_inhibitor (manager);
792         }
793 }
794 
795 static void
reboot_or_poweroff_done(GObject * source,GAsyncResult * res,gpointer user_data)796 reboot_or_poweroff_done (GObject      *source,
797                          GAsyncResult *res,
798                          gpointer      user_data)
799 {
800         GsmConsolekit *consolekit = user_data;
801         GVariant *result;
802         GError *error = NULL;
803 
804         result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
805                                            res,
806                                            &error);
807 
808         if (result == NULL) {
809                 if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED)) {
810                         g_warning ("Shutdown failed: %s", error->message);
811                 }
812                 g_error_free (error);
813                 drop_delay_inhibitor (consolekit);
814                 g_debug ("GsmConsolekit: shutdown preparation failed");
815                 consolekit->priv->prepare_for_shutdown_expected = FALSE;
816                 g_signal_emit_by_name (consolekit, "shutdown-prepared", FALSE);
817         } else {
818                 g_variant_unref (result);
819         }
820 }
821 
822 static void
gsm_consolekit_prepare_shutdown(GsmSystem * system,gboolean restart)823 gsm_consolekit_prepare_shutdown (GsmSystem *system,
824                                  gboolean   restart)
825 {
826         GsmConsolekit *consolekit = GSM_CONSOLEKIT (system);
827         GUnixFDList *fd_list;
828         GVariant *res;
829         GError *error = NULL;
830         gint idx;
831 
832         g_debug ("GsmConsolekit: prepare shutdown");
833 
834         res = g_dbus_proxy_call_with_unix_fd_list_sync (consolekit->priv->ck_proxy,
835                                                         "Inhibit",
836                                                         g_variant_new ("(ssss)",
837                                                                        "shutdown",
838                                                                        g_get_user_name (),
839                                                                        "Preparing to end the session",
840                                                                        "delay"),
841                                                         0,
842                                                         G_MAXINT,
843                                                         NULL,
844                                                         &fd_list,
845                                                         NULL,
846                                                         &error);
847         if (res == NULL) {
848                 g_warning ("Failed to get delay inhibitor: %s", error->message);
849                 g_error_free (error);
850                 /* We may fail here with CK and that's ok */
851         } else {
852                 g_variant_get (res, "(h)", &idx);
853 
854                 consolekit->priv->delay_inhibit_fd = g_unix_fd_list_get (fd_list, idx, NULL);
855 
856                 g_debug ("GsmConsolekit: got delay inhibitor, fd = %d", consolekit->priv->delay_inhibit_fd);
857 
858                 g_variant_unref (res);
859                 g_object_unref (fd_list);
860         }
861 
862         consolekit->priv->prepare_for_shutdown_expected = TRUE;
863 
864         g_dbus_proxy_call (consolekit->priv->ck_proxy,
865                            restart ? "Reboot" : "PowerOff",
866                            g_variant_new ("(b)", TRUE),
867                            0,
868                            G_MAXINT,
869                            NULL,
870                            reboot_or_poweroff_done,
871                            consolekit);
872 }
873 
874 static void
gsm_consolekit_complete_shutdown(GsmSystem * system)875 gsm_consolekit_complete_shutdown (GsmSystem *system)
876 {
877         GsmConsolekit *consolekit = GSM_CONSOLEKIT (system);
878 
879         /* remove delay inhibitor, if any */
880         drop_delay_inhibitor (consolekit);
881 }
882 
883 static gboolean
gsm_consolekit_is_last_session_for_user(GsmSystem * system)884 gsm_consolekit_is_last_session_for_user (GsmSystem *system)
885 {
886         return FALSE;
887 }
888 
889 static void
gsm_consolekit_system_init(GsmSystemInterface * iface)890 gsm_consolekit_system_init (GsmSystemInterface *iface)
891 {
892         iface->can_switch_user = gsm_consolekit_can_switch_user;
893         iface->can_stop = gsm_consolekit_can_stop;
894         iface->can_restart = gsm_consolekit_can_restart;
895         iface->can_suspend = gsm_consolekit_can_suspend;
896         iface->can_hibernate = gsm_consolekit_can_hibernate;
897         iface->attempt_stop = gsm_consolekit_attempt_stop;
898         iface->attempt_restart = gsm_consolekit_attempt_restart;
899         iface->suspend = gsm_consolekit_suspend;
900         iface->hibernate = gsm_consolekit_hibernate;
901         iface->set_session_idle = gsm_consolekit_set_session_idle;
902         iface->is_login_session = gsm_consolekit_is_login_session;
903         iface->set_inhibitors = gsm_consolekit_set_inhibitors;
904         iface->prepare_shutdown = gsm_consolekit_prepare_shutdown;
905         iface->complete_shutdown = gsm_consolekit_complete_shutdown;
906         iface->is_last_session_for_user = gsm_consolekit_is_last_session_for_user;
907 }
908 
909 GsmConsolekit *
gsm_consolekit_new(void)910 gsm_consolekit_new (void)
911 {
912         GsmConsolekit *manager;
913 
914         manager = g_object_new (GSM_TYPE_CONSOLEKIT, NULL);
915 
916         return manager;
917 }
918 
919 static void
ck_proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)920 ck_proxy_signal_cb (GDBusProxy  *proxy,
921                     const gchar *sender_name,
922                     const gchar *signal_name,
923                     GVariant    *parameters,
924                     gpointer     user_data)
925 {
926         GsmConsolekit *consolekit = user_data;
927         gboolean is_about_to_shutdown;
928 
929         g_debug ("GsmConsolekit: received ConsoleKit signal: %s", signal_name);
930 
931         if (g_strcmp0 (signal_name, "PrepareForShutdown") != 0) {
932                 g_debug ("GsmConsolekit: ignoring %s signal", signal_name);
933                 return;
934         }
935 
936         g_variant_get (parameters, "(b)", &is_about_to_shutdown);
937         if (!is_about_to_shutdown) {
938                 g_debug ("GsmConsolekit: ignoring %s signal since about-to-shutdown is FALSE", signal_name);
939                 return;
940         }
941 
942         if (consolekit->priv->prepare_for_shutdown_expected) {
943                 g_debug ("GsmConsolekit: shutdown successfully prepared");
944                 g_signal_emit_by_name (consolekit, "shutdown-prepared", TRUE);
945                 consolekit->priv->prepare_for_shutdown_expected = FALSE;
946         }
947 }
948 
949 static void
ck_session_proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)950 ck_session_proxy_signal_cb (GDBusProxy  *proxy,
951                             const gchar *sender_name,
952                             const gchar *signal_name,
953                             GVariant    *parameters,
954                             gpointer     user_data)
955 {
956         GsmConsolekit *consolekit = user_data;
957         gboolean is_active;
958 
959         g_debug ("GsmConsolekit: received ConsoleKit signal: %s", signal_name);
960 
961         if (g_strcmp0 (signal_name, "ActiveChanged") != 0) {
962                 g_debug ("GsmConsolekit: ignoring %s signal", signal_name);
963                 return;
964         }
965 
966         g_variant_get (parameters, "(b)", &is_active);
967         if (consolekit->priv->is_active != is_active) {
968                 g_debug ("GsmConsolekit: session state changed");
969                 consolekit->priv->is_active = is_active;
970                 g_object_notify (G_OBJECT (consolekit), "active");
971         }
972 }
973