1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2012 Bastien Nocera <hadess@hadess.net>
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 of the License, or
8  * (at your option) 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA.
18  *
19  */
20 
21 #include <gio/gio.h>
22 
23 #include "config.h"
24 
25 #include "csd-power-helper.h"
26 
27 #define LOGIND_DBUS_NAME                       "org.freedesktop.login1"
28 #define LOGIND_DBUS_PATH                       "/org/freedesktop/login1"
29 #define LOGIND_DBUS_INTERFACE                  "org.freedesktop.login1.Manager"
30 
31 #define CONSOLEKIT_DBUS_NAME                    "org.freedesktop.ConsoleKit"
32 #define CONSOLEKIT_DBUS_PATH_MANAGER            "/org/freedesktop/ConsoleKit/Manager"
33 #define CONSOLEKIT_DBUS_INTERFACE_MANAGER       "org.freedesktop.ConsoleKit.Manager"
34 
35 #ifdef HAVE_LOGIND
36 
37 static gboolean
use_logind(void)38 use_logind (void)
39 {
40     static gboolean should_use_logind = FALSE;
41     static gsize once_init_value = 0;
42 
43     if (g_once_init_enter (&once_init_value)) {
44         should_use_logind = access("/run/systemd/seats/", F_OK) == 0; // sd_booted ()
45 
46         g_once_init_leave (&once_init_value, 1);
47     }
48 
49     return should_use_logind;
50 }
51 
52 #else /* HAVE_LOGIND */
53 
54 static gboolean
use_logind(void)55 use_logind (void)
56 {
57     return FALSE;
58 }
59 
60 #endif /* HAVE_LOGIND */
61 
62 static void
logind_stop(void)63 logind_stop (void)
64 {
65         GDBusConnection *bus;
66 
67         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
68         g_dbus_connection_call (bus,
69                                 LOGIND_DBUS_NAME,
70                                 LOGIND_DBUS_PATH,
71                                 LOGIND_DBUS_INTERFACE,
72                                 "PowerOff",
73                                 g_variant_new ("(b)", FALSE),
74                                 NULL, 0, G_MAXINT, NULL, NULL, NULL);
75         g_object_unref (bus);
76 }
77 
78 static gboolean
can_power_action(gchar * method_name)79 can_power_action (gchar *method_name)
80 {
81         GDBusConnection *bus;
82         GVariant *res;
83         gchar *rv;
84         gboolean can_action;
85         GError *error = NULL;
86 
87         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
88         res = g_dbus_connection_call_sync (bus,
89                                            LOGIND_DBUS_NAME,
90                                            LOGIND_DBUS_PATH,
91                                            LOGIND_DBUS_INTERFACE,
92                                            method_name,
93                                            NULL,
94                                            G_VARIANT_TYPE_TUPLE,
95                                            0, G_MAXINT, NULL, &error);
96 
97         g_object_unref (bus);
98 
99         if (error) {
100           g_warning ("Calling %s failed: %s", method_name, error->message);
101           g_clear_error (&error);
102 
103           return FALSE;
104         }
105 
106         g_variant_get (res, "(s)", &rv);
107         g_variant_unref (res);
108 
109         can_action = g_strcmp0 (rv, "yes") == 0 ||
110                      g_strcmp0 (rv, "challenge") == 0;
111 
112         if (!can_action) {
113           g_warning ("logind does not support method %s", method_name);
114         }
115 
116         g_free (rv);
117 
118         return can_action;
119 }
120 
121 static void
logind_suspend(gboolean suspend_then_hibernate)122 logind_suspend (gboolean suspend_then_hibernate)
123 {
124 
125 	gchar *method_name = "Suspend";
126 
127 	if (suspend_then_hibernate && can_power_action("CanHibernate")) {
128 		method_name = "SuspendThenHibernate";
129 	}
130 
131         GDBusConnection *bus;
132 
133         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
134         g_dbus_connection_call (bus,
135                                 LOGIND_DBUS_NAME,
136                                 LOGIND_DBUS_PATH,
137                                 LOGIND_DBUS_INTERFACE,
138                                 method_name,
139                                 g_variant_new ("(b)", TRUE),
140                                 NULL, 0, G_MAXINT, NULL, NULL, NULL);
141         g_object_unref (bus);
142 }
143 
144 static void
logind_hybrid_suspend(void)145 logind_hybrid_suspend (void)
146 {
147         GDBusConnection *bus;
148 
149         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
150         g_dbus_connection_call (bus,
151                                 LOGIND_DBUS_NAME,
152                                 LOGIND_DBUS_PATH,
153                                 LOGIND_DBUS_INTERFACE,
154                                 "HybridSleep",
155                                 g_variant_new ("(b)", TRUE),
156                                 NULL, 0, G_MAXINT, NULL, NULL, NULL);
157         g_object_unref (bus);
158 }
159 
160 static void
logind_hibernate(void)161 logind_hibernate (void)
162 {
163         GDBusConnection *bus;
164 
165         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
166         g_dbus_connection_call (bus,
167                                 LOGIND_DBUS_NAME,
168                                 LOGIND_DBUS_PATH,
169                                 LOGIND_DBUS_INTERFACE,
170                                 "Hibernate",
171                                 g_variant_new ("(b)", TRUE),
172                                 NULL, 0, G_MAXINT, NULL, NULL, NULL);
173         g_object_unref (bus);
174 }
175 
176 static void
consolekit_stop_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)177 consolekit_stop_cb (GObject *source_object,
178                     GAsyncResult *res,
179                     gpointer user_data)
180 {
181         GVariant *result;
182         GError *error = NULL;
183 
184         result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
185                                            res,
186                                            &error);
187         if (result == NULL) {
188                 g_warning ("couldn't stop using ConsoleKit: %s",
189                            error->message);
190                 g_error_free (error);
191         } else {
192                 g_variant_unref (result);
193         }
194 }
195 
196 static void
consolekit_stop(void)197 consolekit_stop (void)
198 {
199         GError *error = NULL;
200         GDBusProxy *proxy;
201 
202         /* power down the machine in a safe way */
203         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
204                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
205                                                NULL,
206                                                CONSOLEKIT_DBUS_NAME,
207                                                CONSOLEKIT_DBUS_PATH_MANAGER,
208                                                CONSOLEKIT_DBUS_INTERFACE_MANAGER,
209                                                NULL, &error);
210         if (proxy == NULL) {
211                 g_warning ("cannot connect to ConsoleKit: %s",
212                            error->message);
213                 g_error_free (error);
214                 return;
215         }
216         g_dbus_proxy_call (proxy,
217                            "Stop",
218                            NULL,
219                            G_DBUS_CALL_FLAGS_NONE,
220                            -1, NULL,
221                            consolekit_stop_cb, NULL);
222         g_object_unref (proxy);
223 }
224 
225 static void
consolekit_sleep_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)226 consolekit_sleep_cb (GObject *source_object,
227                      GAsyncResult *res,
228                      gpointer user_data)
229 {
230         GVariant *result;
231         GError *error = NULL;
232 
233         result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
234                                            res,
235                                            &error);
236         if (result == NULL) {
237                 g_warning ("couldn't sleep using ConsoleKit: %s",
238                            error->message);
239                 g_error_free (error);
240         } else {
241                 g_variant_unref (result);
242         }
243 }
244 
245 static void
consolekit_suspend(gboolean suspend_then_hibernate)246 consolekit_suspend (gboolean suspend_then_hibernate)
247 {
248         GError *error = NULL;
249         GDBusProxy *proxy;
250 
251 	gchar *method_name = "Suspend";
252 
253 	if (suspend_then_hibernate && can_power_action("CanHibernate")) {
254 		method_name = "SuspendThenHibernate";
255 	}
256 
257         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
258                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
259                                                NULL,
260                                                CONSOLEKIT_DBUS_NAME,
261                                                CONSOLEKIT_DBUS_PATH_MANAGER,
262                                                CONSOLEKIT_DBUS_INTERFACE_MANAGER,
263                                                NULL, &error);
264         if (proxy == NULL) {
265                 g_warning ("cannot connect to ConsoleKit: %s",
266                            error->message);
267                 g_error_free (error);
268                 return;
269         }
270         g_dbus_proxy_call (proxy,
271                            method_name,
272                            g_variant_new("(b)", TRUE),
273                            G_DBUS_CALL_FLAGS_NONE,
274                            -1, NULL,
275                            consolekit_sleep_cb, NULL);
276         g_object_unref (proxy);
277 }
278 
279 static void
consolekit_hibernate(void)280 consolekit_hibernate (void)
281 {
282         GError *error = NULL;
283         GDBusProxy *proxy;
284 
285         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
286                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
287                                                NULL,
288                                                CONSOLEKIT_DBUS_NAME,
289                                                CONSOLEKIT_DBUS_PATH_MANAGER,
290                                                CONSOLEKIT_DBUS_INTERFACE_MANAGER,
291                                                NULL, &error);
292         if (proxy == NULL) {
293                 g_warning ("cannot connect to ConsoleKit: %s",
294                            error->message);
295                 g_error_free (error);
296                 return;
297         }
298         g_dbus_proxy_call (proxy,
299                            "Hibernate",
300                            g_variant_new("(b)", TRUE),
301                            G_DBUS_CALL_FLAGS_NONE,
302                            -1, NULL,
303                            consolekit_sleep_cb, NULL);
304         g_object_unref (proxy);
305 }
306 
307 static void
consolekit_hybrid_suspend(void)308 consolekit_hybrid_suspend (void)
309 {
310         GError *error = NULL;
311         GDBusProxy *proxy;
312 
313         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
314                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
315                                                NULL,
316                                                CONSOLEKIT_DBUS_NAME,
317                                                CONSOLEKIT_DBUS_PATH_MANAGER,
318                                                CONSOLEKIT_DBUS_INTERFACE_MANAGER,
319                                                NULL, &error);
320         if (proxy == NULL) {
321                 g_warning ("cannot connect to ConsoleKit: %s",
322                            error->message);
323                 g_error_free (error);
324                 return;
325         }
326         g_dbus_proxy_call (proxy,
327                            "HybridSleep",
328                            g_variant_new("(b)", TRUE),
329                            G_DBUS_CALL_FLAGS_NONE,
330                            -1, NULL,
331                            consolekit_sleep_cb, NULL);
332         g_object_unref (proxy);
333 }
334 
335 void
csd_power_suspend(gboolean try_hybrid,gboolean suspend_then_hibernate)336 csd_power_suspend (gboolean try_hybrid, gboolean suspend_then_hibernate)
337 {
338   if (use_logind ()) {
339     if (try_hybrid && can_power_action ("CanHybridSleep")) {
340       logind_hybrid_suspend ();
341     }
342     else {
343       logind_suspend (suspend_then_hibernate);
344     }
345   }
346   else {
347     if (try_hybrid && can_power_action ("CanHybridSleep")) {
348       consolekit_hybrid_suspend ();
349     }
350     else {
351       consolekit_suspend (suspend_then_hibernate);
352     }
353   }
354 }
355 
356 void
csd_power_poweroff(void)357 csd_power_poweroff (void)
358 {
359   if (use_logind ()) {
360     logind_stop ();
361   }
362   else {
363     consolekit_stop ();
364   }
365 }
366 
367 void
csd_power_hibernate(void)368 csd_power_hibernate (void)
369 {
370   if (use_logind ()) {
371     logind_hibernate ();
372   }
373   else {
374     consolekit_hibernate ();
375   }
376 }
377