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 #ifdef HAVE_OLD_UPOWER
35 #define UPOWER_ENABLE_DEPRECATED 1
36 #include <upower.h>
37 #endif
38 
39 #include "csm-system.h"
40 #include "csm-consolekit.h"
41 
42 #define CK_NAME      "org.freedesktop.ConsoleKit"
43 
44 #define CK_MANAGER_PATH         "/org/freedesktop/ConsoleKit/Manager"
45 #define CK_MANAGER_INTERFACE    CK_NAME ".Manager"
46 #define CK_SEAT_INTERFACE       CK_NAME ".Seat"
47 #define CK_SESSION_INTERFACE    CK_NAME ".Session"
48 
49 
50 struct _CsmConsolekitPrivate
51 {
52         GDBusProxy      *ck_proxy;
53         GDBusProxy      *ck_session_proxy;
54 #ifdef HAVE_OLD_UPOWER
55         UpClient        *up_client;
56 #endif
57         char            *session_id;
58         gchar           *session_path;
59 
60         GSList          *inhibitors;
61         gint             inhibit_fd;
62 
63         gboolean         is_active;
64 
65         gint             delay_inhibit_fd;
66         gboolean         prepare_for_shutdown_expected;
67 };
68 
69 enum {
70         PROP_0,
71         PROP_ACTIVE
72 };
73 
74 static void csm_consolekit_system_init (CsmSystemInterface *iface);
75 
G_DEFINE_TYPE_WITH_CODE(CsmConsolekit,csm_consolekit,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (CSM_TYPE_SYSTEM,csm_consolekit_system_init))76 G_DEFINE_TYPE_WITH_CODE (CsmConsolekit, csm_consolekit, G_TYPE_OBJECT,
77                          G_IMPLEMENT_INTERFACE (CSM_TYPE_SYSTEM,
78                                                 csm_consolekit_system_init))
79 
80 static void
81 drop_system_inhibitor (CsmConsolekit *manager)
82 {
83         if (manager->priv->inhibit_fd != -1) {
84                 g_debug ("CsmConsolekit: Dropping system inhibitor");
85                 close (manager->priv->inhibit_fd);
86                 manager->priv->inhibit_fd = -1;
87         }
88 }
89 
90 static void
drop_delay_inhibitor(CsmConsolekit * manager)91 drop_delay_inhibitor (CsmConsolekit *manager)
92 {
93         if (manager->priv->delay_inhibit_fd != -1) {
94                 g_debug ("CsmConsolekit: Dropping delay inhibitor");
95                 close (manager->priv->delay_inhibit_fd);
96                 manager->priv->delay_inhibit_fd = -1;
97         }
98 }
99 
100 static void
csm_consolekit_finalize(GObject * object)101 csm_consolekit_finalize (GObject *object)
102 {
103         CsmConsolekit *consolekit = CSM_CONSOLEKIT (object);
104 
105         g_clear_object (&consolekit->priv->ck_proxy);
106         g_clear_object (&consolekit->priv->ck_session_proxy);
107         free (consolekit->priv->session_id);
108         g_free (consolekit->priv->session_path);
109 
110         if (consolekit->priv->inhibitors != NULL) {
111                 g_slist_free_full (consolekit->priv->inhibitors, g_free);
112         }
113         drop_system_inhibitor (consolekit);
114         drop_delay_inhibitor (consolekit);
115 
116 #ifdef HAVE_OLD_UPOWER
117         g_clear_object (&manager->priv->up_client);
118 #endif
119 
120         G_OBJECT_CLASS (csm_consolekit_parent_class)->finalize (object);
121 }
122 
123 static void
csm_consolekit_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)124 csm_consolekit_set_property (GObject      *object,
125                              guint         prop_id,
126                              const GValue *value,
127                              GParamSpec   *pspec)
128 {
129         CsmConsolekit *self = CSM_CONSOLEKIT (object);
130 
131         switch (prop_id) {
132         case PROP_ACTIVE:
133                 self->priv->is_active = g_value_get_boolean (value);
134                 break;
135         default:
136                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137         }
138 }
139 
140 static void
csm_consolekit_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)141 csm_consolekit_get_property (GObject    *object,
142                              guint       prop_id,
143                              GValue     *value,
144                              GParamSpec *pspec)
145 {
146         CsmConsolekit *self = CSM_CONSOLEKIT (object);
147 
148         switch (prop_id) {
149         case PROP_ACTIVE:
150                 g_value_set_boolean (value, self->priv->is_active);
151                 break;
152         default:
153                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154                 break;
155         }
156 }
157 
158 static void
csm_consolekit_class_init(CsmConsolekitClass * manager_class)159 csm_consolekit_class_init (CsmConsolekitClass *manager_class)
160 {
161         GObjectClass *object_class;
162 
163         object_class = G_OBJECT_CLASS (manager_class);
164 
165         object_class->get_property = csm_consolekit_get_property;
166         object_class->set_property = csm_consolekit_set_property;
167         object_class->finalize = csm_consolekit_finalize;
168 
169         g_object_class_override_property (object_class, PROP_ACTIVE, "active");
170 
171         g_type_class_add_private (manager_class, sizeof (CsmConsolekitPrivate));
172 }
173 
174 static void ck_session_proxy_signal_cb (GDBusProxy  *proxy,
175                                         const gchar *sender_name,
176                                         const gchar *signal_name,
177                                         GVariant    *parameters,
178                                         gpointer     user_data);
179 
180 static void ck_proxy_signal_cb (GDBusProxy  *proxy,
181                                 const gchar *sender_name,
182                                 const gchar *signal_name,
183                                 GVariant    *parameters,
184                                 gpointer     user_data);
185 
186 static void
ck_pid_get_session(CsmConsolekit * manager,pid_t pid,gchar ** session_id)187 ck_pid_get_session (CsmConsolekit *manager,
188                     pid_t          pid,
189                     gchar        **session_id)
190 {
191         GVariant *res;
192 
193         *session_id = NULL;
194 
195         if (pid < 0) {
196                 g_warning ("Calling GetSessionForUnixProcess failed."
197                            "Invalid pid.");
198                 return;
199         }
200 
201         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
202                                       "GetSessionForUnixProcess",
203                                       g_variant_new ("(u)", pid),
204                                       0,
205                                       -1,
206                                       NULL,
207                                       NULL);
208         if (!res) {
209                 g_warning ("Calling GetSessionForUnixProcess failed."
210                            "Check that ConsoleKit is properly installed.");
211                 return;
212         }
213 
214         g_variant_get (res, "(o)", session_id);
215         g_variant_unref (res);
216 }
217 
218 static void
csm_consolekit_init(CsmConsolekit * manager)219 csm_consolekit_init (CsmConsolekit *manager)
220 {
221         GError *error = NULL;
222         GDBusConnection *bus;
223         GVariant *res;
224 
225         manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
226                                                      CSM_TYPE_CONSOLEKIT,
227                                                      CsmConsolekitPrivate);
228 
229         manager->priv->inhibit_fd = -1;
230         manager->priv->delay_inhibit_fd = -1;
231 
232         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
233         if (bus == NULL)
234                 g_error ("Failed to connect to system bus: %s",
235                          error->message);
236         manager->priv->ck_proxy =
237                 g_dbus_proxy_new_sync (bus,
238                                        0,
239                                        NULL,
240                                        CK_NAME,
241                                        CK_MANAGER_PATH,
242                                        CK_MANAGER_INTERFACE,
243                                        NULL,
244                                        &error);
245         if (manager->priv->ck_proxy == NULL) {
246                 g_warning ("Failed to connect to consolekit: %s",
247                            error->message);
248                 g_clear_error (&error);
249         }
250 
251         g_signal_connect (manager->priv->ck_proxy, "g-signal",
252                           G_CALLBACK (ck_proxy_signal_cb), manager);
253 
254         ck_pid_get_session (manager, getpid (), &manager->priv->session_id);
255 
256         if (manager->priv->session_id == NULL) {
257                 g_warning ("Could not get session id for session. Check that ConsoleKit is "
258                            "properly installed.");
259                 return;
260         }
261 
262         /* in ConsoleKit, the session id is the session path */
263         manager->priv->session_path = g_strdup (manager->priv->session_id);
264 
265         manager->priv->ck_session_proxy =
266                 g_dbus_proxy_new_sync (bus,
267                                        0,
268                                        NULL,
269                                        CK_NAME,
270                                        manager->priv->session_path,
271                                        CK_SESSION_INTERFACE,
272                                        NULL,
273                                        &error);
274         if (manager->priv->ck_proxy == NULL) {
275                 g_warning ("Failed to connect to consolekit session: %s",
276                            error->message);
277                 g_clear_error (&error);
278         }
279 
280         g_signal_connect (manager->priv->ck_session_proxy, "g-signal",
281                           G_CALLBACK (ck_session_proxy_signal_cb), manager);
282 
283 #ifdef HAVE_OLD_UPOWER
284         g_clear_object (&manager->priv->up_client);
285         manager->priv->up_client = up_client_new ();
286 #endif
287 
288         g_object_unref (bus);
289 }
290 
291 static void
emit_restart_complete(CsmConsolekit * manager,GError * error)292 emit_restart_complete (CsmConsolekit *manager,
293                        GError     *error)
294 {
295         GError *call_error;
296 
297         call_error = NULL;
298 
299         if (error != NULL) {
300                 call_error = g_error_new_literal (CSM_SYSTEM_ERROR,
301                                                   CSM_SYSTEM_ERROR_RESTARTING,
302                                                   error->message);
303         }
304 
305         g_signal_emit_by_name (G_OBJECT (manager),
306                                "request_completed", call_error);
307 
308         if (call_error != NULL) {
309                 g_error_free (call_error);
310         }
311 }
312 
313 static void
emit_stop_complete(CsmConsolekit * manager,GError * error)314 emit_stop_complete (CsmConsolekit *manager,
315                     GError     *error)
316 {
317         GError *call_error;
318 
319         call_error = NULL;
320 
321         if (error != NULL) {
322                 call_error = g_error_new_literal (CSM_SYSTEM_ERROR,
323                                                   CSM_SYSTEM_ERROR_STOPPING,
324                                                   error->message);
325         }
326 
327         g_signal_emit_by_name (G_OBJECT (manager),
328                                "request_completed", call_error);
329 
330         if (call_error != NULL) {
331                 g_error_free (call_error);
332         }
333 }
334 
335 static void
restart_done(GObject * source,GAsyncResult * result,gpointer user_data)336 restart_done (GObject      *source,
337               GAsyncResult *result,
338               gpointer      user_data)
339 {
340         GDBusProxy *proxy = G_DBUS_PROXY (source);
341         CsmConsolekit *manager = user_data;
342         GError *error = NULL;
343         GVariant *res;
344 
345         res = g_dbus_proxy_call_finish (proxy, result, &error);
346 
347         if (!res) {
348                 g_warning ("Unable to restart system: %s", error->message);
349                 emit_restart_complete (manager, error);
350                 g_error_free (error);
351         } else {
352                 emit_restart_complete (manager, NULL);
353                 g_variant_unref (res);
354         }
355 }
356 
357 static void
csm_consolekit_attempt_restart(CsmSystem * system)358 csm_consolekit_attempt_restart (CsmSystem *system)
359 {
360         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
361 
362         /* Use Restart instead of Reboot because it will work on
363          * both CK and CK2 */
364         g_dbus_proxy_call (manager->priv->ck_proxy,
365                            "Restart",
366                            g_variant_new ("()"),
367                            0,
368                            G_MAXINT,
369                            NULL,
370                            restart_done,
371                            manager);
372 }
373 
374 static void
stop_done(GObject * source,GAsyncResult * result,gpointer user_data)375 stop_done (GObject      *source,
376            GAsyncResult *result,
377            gpointer      user_data)
378 {
379         GDBusProxy *proxy = G_DBUS_PROXY (source);
380         CsmConsolekit *manager = user_data;
381         GError *error = NULL;
382         GVariant *res;
383 
384         res = g_dbus_proxy_call_finish (proxy, result, &error);
385 
386         if (!res) {
387                 g_warning ("Unable to stop system: %s", error->message);
388                 emit_stop_complete (manager, error);
389                 g_error_free (error);
390         } else {
391                 emit_stop_complete (manager, NULL);
392                 g_variant_unref (res);
393         }
394 }
395 
396 static void
csm_consolekit_attempt_stop(CsmSystem * system)397 csm_consolekit_attempt_stop (CsmSystem *system)
398 {
399         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
400 
401         /* Use Stop insetad of PowerOff because it will work with
402          * Ck and CK2. */
403         g_dbus_proxy_call (manager->priv->ck_proxy,
404                            "Stop",
405                            g_variant_new ("()"),
406                            0,
407                            G_MAXINT,
408                            NULL,
409                            stop_done,
410                            manager);
411 }
412 
413 static void
csm_consolekit_set_session_idle(CsmSystem * system,gboolean is_idle)414 csm_consolekit_set_session_idle (CsmSystem *system,
415                               gboolean   is_idle)
416 {
417         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
418 
419         g_debug ("Updating consolekit idle status: %d", is_idle);
420         g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
421                                 "SetIdleHint",
422                                 g_variant_new ("(b)", is_idle),
423                                 0,
424                                 G_MAXINT,
425                                 NULL, NULL);
426 }
427 
428 static void
ck_session_get_seat(CsmConsolekit * manager,gchar ** seat)429 ck_session_get_seat (CsmConsolekit *manager,
430                      gchar        **seat)
431 {
432         GVariant *res;
433 
434         *seat = NULL;
435 
436         res = g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
437                                       "GetSeatId",
438                                       g_variant_new ("()"),
439                                       0,
440                                       -1,
441                                       NULL, NULL);
442         if (!res) {
443                 g_warning ("CsmConsoleKit: Calling GetSeatId failed.");
444                 return;
445         }
446 
447         g_variant_get (res, "(o)", seat);
448         g_variant_unref (res);
449 }
450 
451 /* returns -1 on failure
452  *          0 seat is multi-session
453  *          1 seat is not multi-session
454  */
455 static gint
ck_seat_can_multi_session(CsmConsolekit * manager,const gchar * seat)456 ck_seat_can_multi_session (CsmConsolekit *manager,
457                            const gchar   *seat)
458 {
459         GDBusConnection *bus;
460         GVariant *res;
461         gboolean  can_activate;
462 
463 
464         bus = g_dbus_proxy_get_connection (manager->priv->ck_proxy);
465         res = g_dbus_connection_call_sync (bus,
466                                            CK_NAME,
467                                            seat,
468                                            CK_SEAT_INTERFACE,
469                                            "CanActivateSessions",
470                                            g_variant_new ("()"),
471                                            G_VARIANT_TYPE_BOOLEAN,
472                                            0,
473                                            -1,
474                                            NULL, NULL);
475         if (!res) {
476                 g_warning ("CsmConsoleKit: Calling GetSeatId failed.");
477                 return -1;
478         }
479 
480         g_variant_get (res, "(b)", &can_activate);
481         g_variant_unref (res);
482 
483         return can_activate == TRUE ? 1 : 0;
484 }
485 
486 static gboolean
csm_consolekit_can_switch_user(CsmSystem * system)487 csm_consolekit_can_switch_user (CsmSystem *system)
488 {
489         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
490         gchar *seat;
491         gint ret;
492 
493         ck_session_get_seat (manager, &seat);
494         ret = ck_seat_can_multi_session (manager, seat);
495         free (seat);
496 
497         return ret > 0;
498 }
499 
500 static gboolean
csm_consolekit_can_restart(CsmSystem * system)501 csm_consolekit_can_restart (CsmSystem *system)
502 {
503         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
504         GVariant *res;
505         gboolean can_restart;
506 
507         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
508                                       "CanRestart",
509                                       g_variant_new ("()"),
510                                       0,
511                                       G_MAXINT,
512                                       NULL,
513                                       NULL);
514         if (!res) {
515                 g_warning ("Calling CanRestart failed. Check that ConsoleKit is "
516                            "properly installed.");
517                 return FALSE;
518         }
519 
520         g_variant_get (res, "(b)", &can_restart);
521         g_variant_unref (res);
522 
523         return can_restart;
524 }
525 
526 static gboolean
csm_consolekit_can_stop(CsmSystem * system)527 csm_consolekit_can_stop (CsmSystem *system)
528 {
529         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
530         GVariant *res;
531         gboolean can_stop;
532 
533         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
534                                       "CanStop",
535                                       g_variant_new ("()"),
536                                       0,
537                                       G_MAXINT,
538                                       NULL,
539                                       NULL);
540         if (!res) {
541                 g_warning ("Calling CanStop failed. Check that ConsoleKit is "
542                            "properly installed.");
543                 return FALSE;
544         }
545 
546         g_variant_get (res, "(b)", &can_stop);
547         g_variant_unref (res);
548 
549         return can_stop;
550 }
551 
552 /* returns -1 on failure, 0 on success */
553 static gint
ck_session_get_class(CsmConsolekit * manager,gchar ** session_class)554 ck_session_get_class (CsmConsolekit *manager,
555                       gchar        **session_class)
556 {
557         GVariant *res;
558 
559         *session_class = NULL;
560 
561         res = g_dbus_proxy_call_sync (manager->priv->ck_session_proxy,
562                                       "GetSessionClass",
563                                       g_variant_new ("()"),
564                                       0,
565                                       -1,
566                                       NULL, NULL);
567         if (!res) {
568                 g_warning ("CsmConsoleKit: Calling GetSessionClass failed.");
569                 return -1;
570         }
571 
572         g_variant_get (res, "(s)", session_class);
573         g_variant_unref (res);
574 
575         return 0;
576 }
577 
578 static gboolean
csm_consolekit_is_login_session(CsmSystem * system)579 csm_consolekit_is_login_session (CsmSystem *system)
580 {
581         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
582         int res;
583         gboolean ret;
584         gchar *session_class = NULL;
585 
586         ret = FALSE;
587 
588         if (manager->priv->session_id == NULL) {
589                 return ret;
590         }
591 
592         res = ck_session_get_class (manager, &session_class);
593         if (res < 0) {
594                 g_warning ("Could not get session class: %s", strerror (-res));
595                 return FALSE;
596         }
597         ret = (g_strcmp0 (session_class, "greeter") == 0);
598         g_free (session_class);
599 
600         return ret;
601 }
602 
603 static gboolean
csm_consolekit_can_suspend(CsmSystem * system)604 csm_consolekit_can_suspend (CsmSystem *system)
605 {
606 #ifdef HAVE_OLD_UPOWER
607         CsmConsolekit *consolekit = CSM_CONSOLEKIT (system);
608         return up_client_get_can_suspend (consolekit->priv->up_client);
609 #else
610         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
611         gchar *rv;
612         GVariant *res;
613         gboolean can_suspend;
614 
615         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
616                                       "CanSuspend",
617                                       NULL,
618                                       0,
619                                       G_MAXINT,
620                                       NULL,
621                                       NULL);
622         if (!res) {
623                 g_warning ("Calling CanSuspend failed. Check that ConsoleKit is "
624                            "properly installed.");
625                 return FALSE;
626         }
627 
628         g_variant_get (res, "(s)", &rv);
629         g_variant_unref (res);
630 
631         can_suspend = g_strcmp0 (rv, "yes") == 0 ||
632                       g_strcmp0 (rv, "challenge") == 0;
633 
634         g_free (rv);
635 
636         return can_suspend;
637 #endif
638 }
639 
640 static gboolean
csm_consolekit_can_hibernate(CsmSystem * system)641 csm_consolekit_can_hibernate (CsmSystem *system)
642 {
643 #ifdef HAVE_OLD_UPOWER
644         CsmConsolekit *consolekit = CSM_CONSOLEKIT (system);
645         return up_client_get_can_hibernate (consolekit->priv->up_client);
646 #else
647         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
648         gchar *rv;
649         GVariant *res;
650         gboolean can_hibernate;
651 
652         res = g_dbus_proxy_call_sync (manager->priv->ck_proxy,
653                                       "CanHibernate",
654                                       NULL,
655                                       0,
656                                       G_MAXINT,
657                                       NULL,
658                                       NULL);
659         if (!res) {
660                 g_warning ("Calling CanHibernate failed. Check that ConsoleKit is "
661                            "properly installed.");
662                 return FALSE;
663         }
664 
665         g_variant_get (res, "(s)", &rv);
666         g_variant_unref (res);
667 
668         can_hibernate = g_strcmp0 (rv, "yes") == 0 ||
669                         g_strcmp0 (rv, "challenge") == 0;
670 
671         g_free (rv);
672 
673         return can_hibernate;
674 #endif
675 }
676 
677 static void
suspend_done(GObject * source,GAsyncResult * result,gpointer user_data)678 suspend_done (GObject      *source,
679               GAsyncResult *result,
680               gpointer      user_data)
681 {
682         GDBusProxy *proxy = G_DBUS_PROXY (source);
683         GError *error = NULL;
684         GVariant *res;
685 
686         res = g_dbus_proxy_call_finish (proxy, result, &error);
687 
688         if (!res) {
689                 g_warning ("Unable to suspend system: %s", error->message);
690                 g_error_free (error);
691         } else {
692                 g_variant_unref (res);
693         }
694 }
695 
696 static void
hibernate_done(GObject * source,GAsyncResult * result,gpointer user_data)697 hibernate_done (GObject      *source,
698                 GAsyncResult *result,
699                 gpointer      user_data)
700 {
701         GDBusProxy *proxy = G_DBUS_PROXY (source);
702         GError *error = NULL;
703         GVariant *res;
704 
705         res = g_dbus_proxy_call_finish (proxy, result, &error);
706 
707         if (!res) {
708                 g_warning ("Unable to hibernate system: %s", error->message);
709                 g_error_free (error);
710         } else {
711                 g_variant_unref (res);
712         }
713 }
714 
715 static void
csm_consolekit_suspend(CsmSystem * system,gboolean suspend_then_hibernate)716 csm_consolekit_suspend (CsmSystem *system, gboolean suspend_then_hibernate)
717 {
718 #ifdef HAVE_OLD_UPOWER
719         CsmConsolekit *consolekit = CSM_CONSOLEKIT (system);
720         GError *error = NULL;
721         gboolean ret;
722 
723         ret = up_client_suspend_sync (consolekit->priv->up_client, NULL, &error);
724         if (!ret) {
725                 g_warning ("Unexpected suspend failure: %s", error->message);
726                 g_error_free (error);
727         }
728 #else
729         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
730 
731 	gchar *method = "Suspend";
732 	if (suspend_then_hibernate && csm_consolekit_can_suspend (system) && csm_consolekit_can_hibernate (system)) {
733 		method = "SuspendThenHibernate";
734 	}
735 	g_debug ("Suspend using: %s", method);
736 
737         g_dbus_proxy_call (manager->priv->ck_proxy,
738                            method,
739                            g_variant_new ("(b)", TRUE),
740                            0,
741                            G_MAXINT,
742                            NULL,
743                            suspend_done,
744                            manager);
745 #endif
746 }
747 
748 static void
csm_consolekit_hibernate(CsmSystem * system)749 csm_consolekit_hibernate (CsmSystem *system)
750 {
751 #ifdef HAVE_OLD_UPOWER
752         CsmConsolekit *consolekit = CSM_CONSOLEKIT (system);
753         GError *error = NULL;
754         gboolean ret;
755 
756         ret = up_client_hibernate_sync (consolekit->priv->up_client, NULL, &error);
757         if (!ret) {
758                 g_warning ("Unexpected hibernate failure: %s", error->message);
759                 g_error_free (error);
760         }
761 #else
762         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
763 
764         g_dbus_proxy_call (manager->priv->ck_proxy,
765                            "Hibernate",
766                            g_variant_new ("(b)", TRUE),
767                            0,
768                            G_MAXINT,
769                            NULL,
770                            hibernate_done,
771                            manager);
772 #endif
773 }
774 
775 static void
inhibit_done(GObject * source,GAsyncResult * result,gpointer user_data)776 inhibit_done (GObject      *source,
777               GAsyncResult *result,
778               gpointer      user_data)
779 {
780         GDBusProxy *proxy = G_DBUS_PROXY (source);
781         CsmConsolekit *manager = CSM_CONSOLEKIT (user_data);
782         GError *error = NULL;
783         GVariant *res;
784         GUnixFDList *fd_list = NULL;
785         gint idx;
786 
787         res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
788 
789         if (!res) {
790                 g_warning ("Unable to inhibit system: %s", error->message);
791                 g_error_free (error);
792         } else {
793                 g_variant_get (res, "(h)", &idx);
794                 manager->priv->inhibit_fd = g_unix_fd_list_get (fd_list, idx, &error);
795                 if (manager->priv->inhibit_fd == -1) {
796                         g_warning ("Failed to receive system inhibitor fd: %s", error->message);
797                         g_error_free (error);
798                 }
799                 g_debug ("System inhibitor fd is %d", manager->priv->inhibit_fd);
800                 g_object_unref (fd_list);
801                 g_variant_unref (res);
802         }
803 
804         if (manager->priv->inhibitors == NULL) {
805                 drop_system_inhibitor (manager);
806         }
807 }
808 
809 static void
csm_consolekit_add_inhibitor(CsmSystem * system,const gchar * id,CsmInhibitorFlag flag)810 csm_consolekit_add_inhibitor (CsmSystem        *system,
811                            const gchar      *id,
812                            CsmInhibitorFlag  flag)
813 {
814         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
815 
816         if ((flag & CSM_INHIBITOR_FLAG_SUSPEND) == 0)
817                 return;
818 
819         if (manager->priv->inhibitors == NULL) {
820                 g_debug ("Adding system inhibitor");
821                 g_dbus_proxy_call_with_unix_fd_list (manager->priv->ck_proxy,
822                                                      "Inhibit",
823                                                      g_variant_new ("(ssss)",
824                                                                     "sleep:shutdown",
825                                                                     g_get_user_name (),
826                                                                     "user session inhibited",
827                                                                     "block"),
828                                                      0,
829                                                      G_MAXINT,
830                                                      NULL,
831                                                      NULL,
832                                                      inhibit_done,
833                                                      manager);
834         }
835         manager->priv->inhibitors = g_slist_prepend (manager->priv->inhibitors, g_strdup (id));
836 }
837 
838 static void
csm_consolekit_remove_inhibitor(CsmSystem * system,const gchar * id)839 csm_consolekit_remove_inhibitor (CsmSystem   *system,
840                               const gchar *id)
841 {
842         CsmConsolekit *manager = CSM_CONSOLEKIT (system);
843         GSList *l;
844 
845         l = g_slist_find_custom (manager->priv->inhibitors, id, (GCompareFunc)g_strcmp0);
846         if (l == NULL)
847                 return;
848 
849         g_free (l->data);
850         manager->priv->inhibitors = g_slist_delete_link (manager->priv->inhibitors, l);
851         if (manager->priv->inhibitors == NULL) {
852                 drop_system_inhibitor (manager);
853         }
854 }
855 
856 static gboolean
csm_consolekit_is_last_session_for_user(CsmSystem * system)857 csm_consolekit_is_last_session_for_user (CsmSystem *system)
858 {
859         return FALSE;
860 }
861 
862 static void
csm_consolekit_system_init(CsmSystemInterface * iface)863 csm_consolekit_system_init (CsmSystemInterface *iface)
864 {
865         iface->can_switch_user = csm_consolekit_can_switch_user;
866         iface->can_stop = csm_consolekit_can_stop;
867         iface->can_restart = csm_consolekit_can_restart;
868         iface->can_suspend = csm_consolekit_can_suspend;
869         iface->can_hibernate = csm_consolekit_can_hibernate;
870         iface->attempt_stop = csm_consolekit_attempt_stop;
871         iface->attempt_restart = csm_consolekit_attempt_restart;
872         iface->suspend = csm_consolekit_suspend;
873         iface->hibernate = csm_consolekit_hibernate;
874         iface->set_session_idle = csm_consolekit_set_session_idle;
875         iface->is_login_session = csm_consolekit_is_login_session;
876         iface->add_inhibitor = csm_consolekit_add_inhibitor;
877         iface->remove_inhibitor = csm_consolekit_remove_inhibitor;
878         iface->is_last_session_for_user = csm_consolekit_is_last_session_for_user;
879 }
880 
881 CsmConsolekit *
csm_consolekit_new(void)882 csm_consolekit_new (void)
883 {
884         CsmConsolekit *manager;
885 
886         manager = g_object_new (CSM_TYPE_CONSOLEKIT, NULL);
887 
888         return manager;
889 }
890 
891 static void
ck_proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)892 ck_proxy_signal_cb (GDBusProxy  *proxy,
893                     const gchar *sender_name,
894                     const gchar *signal_name,
895                     GVariant    *parameters,
896                     gpointer     user_data)
897 {
898         CsmConsolekit *consolekit = user_data;
899         gboolean is_about_to_shutdown;
900 
901         g_debug ("CsmConsolekit: received ConsoleKit signal: %s", signal_name);
902 
903         if (g_strcmp0 (signal_name, "PrepareForShutdown") != 0) {
904                 g_debug ("CsmConsolekit: ignoring %s signal", signal_name);
905                 return;
906         }
907 
908         g_variant_get (parameters, "(b)", &is_about_to_shutdown);
909         if (!is_about_to_shutdown) {
910                 g_debug ("CsmConsolekit: ignoring %s signal since about-to-shutdown is FALSE", signal_name);
911                 return;
912         }
913 
914         if (consolekit->priv->prepare_for_shutdown_expected) {
915                 g_debug ("CsmConsolekit: shutdown successfully prepared");
916                 g_signal_emit_by_name (consolekit, "shutdown-prepared", TRUE);
917                 consolekit->priv->prepare_for_shutdown_expected = FALSE;
918         }
919 }
920 
921 static void
ck_session_proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)922 ck_session_proxy_signal_cb (GDBusProxy  *proxy,
923                             const gchar *sender_name,
924                             const gchar *signal_name,
925                             GVariant    *parameters,
926                             gpointer     user_data)
927 {
928         CsmConsolekit *consolekit = user_data;
929         gboolean is_active;
930 
931         g_debug ("CsmConsolekit: received ConsoleKit signal: %s", signal_name);
932 
933         if (g_strcmp0 (signal_name, "ActiveChanged") != 0) {
934                 g_debug ("CsmConsolekit: ignoring %s signal", signal_name);
935                 return;
936         }
937 
938         g_variant_get (parameters, "(b)", &is_active);
939         if (consolekit->priv->is_active != is_active) {
940                 g_debug ("CsmConsolekit: session state changed");
941                 consolekit->priv->is_active = is_active;
942                 g_object_notify (G_OBJECT (consolekit), "active");
943         }
944 }
945