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