upower/src/up-daemon.c
Richard Hughes 0fd304c3f3 Add new NotifySleep() and NotifyResume() signals that include the sleep type
This allows session power managers to do different actions depending on whether the user
is suspending or hibernating. This allows the session policy agent to poke other things
(for instance, the screensaver) even if another process initiated the sleep.

This is based on a patch from Phillip Susi <psusi@cfl.rr.com>, many thanks.
2011-05-04 17:57:13 +01:00

1426 lines
37 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
* Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gi18n-lib.h>
#include <glib-object.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "up-config.h"
#include "up-polkit.h"
#include "up-device-list.h"
#include "up-device.h"
#include "up-backend.h"
#include "up-daemon.h"
#include "up-daemon-glue.h"
#include "up-marshal.h"
enum
{
PROP_0,
PROP_DAEMON_VERSION,
PROP_CAN_SUSPEND,
PROP_CAN_HIBERNATE,
PROP_ON_BATTERY,
PROP_ON_LOW_BATTERY,
PROP_LID_IS_CLOSED,
PROP_LID_IS_PRESENT,
PROP_LID_FORCE_SLEEP,
PROP_IS_DOCKED,
PROP_LAST
};
enum
{
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_DEVICE_CHANGED,
SIGNAL_CHANGED,
SIGNAL_SLEEPING,
SIGNAL_RESUMING,
SIGNAL_NOTIFY_SLEEP,
SIGNAL_NOTIFY_RESUME,
SIGNAL_LAST,
};
static guint signals[SIGNAL_LAST] = { 0 };
struct UpDaemonPrivate
{
DBusGConnection *connection;
DBusGProxy *proxy;
UpConfig *config;
UpPolkit *polkit;
UpBackend *backend;
UpDeviceList *power_devices;
gboolean on_battery;
gboolean on_low_battery;
gboolean lid_is_closed;
gboolean lid_is_present;
gboolean lid_force_sleep;
gboolean is_docked;
gboolean kernel_can_suspend;
gboolean kernel_can_hibernate;
gboolean hibernate_has_encrypted_swap;
gboolean during_coldplug;
gboolean sent_sleeping_signal;
guint battery_poll_id;
guint battery_poll_count;
GTimer *about_to_sleep_timer;
guint about_to_sleep_id;
guint conf_sleep_timeout;
gboolean conf_allow_hibernate_encrypted_swap;
const gchar *sleep_kind;
};
static void up_daemon_finalize (GObject *object);
static gboolean up_daemon_get_on_battery_local (UpDaemon *daemon);
static gboolean up_daemon_get_on_low_battery_local (UpDaemon *daemon);
static gboolean up_daemon_get_on_ac_local (UpDaemon *daemon);
G_DEFINE_TYPE (UpDaemon, up_daemon, G_TYPE_OBJECT)
#define UP_DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DAEMON, UpDaemonPrivate))
/* if using more memory compared to usable swap, disable hibernate */
/* Native Linux suspend-to-disk does not use compression, and needs 2 KB of
* page meta information for each MB of active memory. Add some error margin
* here, though. */
#define UP_DAEMON_SWAP_WATERLINE 98.f /* % */
/* refresh all the devices after this much time when on-battery has changed */
#define UP_DAEMON_ON_BATTERY_REFRESH_DEVICES_DELAY 1 /* seconds */
#define UP_DAEMON_POLL_BATTERY_NUMBER_TIMES 5
/* D-BUS to connect to. Can be set to session bus for testing */
static DBusBusType daemon_bus_type = DBUS_BUS_SYSTEM;
DBusBusType
up_daemon_get_bus_type (void)
{
return daemon_bus_type;
}
void
up_daemon_set_bus_type (DBusBusType type)
{
daemon_bus_type = type;
}
/**
* up_daemon_get_on_battery_local:
*
* As soon as _any_ battery goes discharging, this is true
**/
static gboolean
up_daemon_get_on_battery_local (UpDaemon *daemon)
{
guint i;
gboolean ret;
gboolean result = FALSE;
gboolean on_battery;
UpDevice *device;
GPtrArray *array;
/* ask each device */
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
ret = up_device_get_on_battery (device, &on_battery);
if (ret && on_battery) {
result = TRUE;
break;
}
}
g_ptr_array_unref (array);
return result;
}
/**
* up_daemon_get_number_devices_of_type:
**/
guint
up_daemon_get_number_devices_of_type (UpDaemon *daemon, UpDeviceKind type)
{
guint i;
UpDevice *device;
GPtrArray *array;
UpDeviceKind type_tmp;
guint count = 0;
/* ask each device */
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
g_object_get (device,
"type", &type_tmp,
NULL);
if (type == type_tmp)
count++;
}
g_ptr_array_unref (array);
return count;
}
/**
* up_daemon_get_on_low_battery_local:
*
* As soon as _all_ batteries are low, this is true
**/
static gboolean
up_daemon_get_on_low_battery_local (UpDaemon *daemon)
{
guint i;
gboolean ret;
gboolean result = TRUE;
gboolean on_low_battery;
UpDevice *device;
GPtrArray *array;
/* ask each device */
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
ret = up_device_get_low_battery (device, &on_low_battery);
if (ret && !on_low_battery) {
result = FALSE;
break;
}
}
g_ptr_array_unref (array);
return result;
}
/**
* up_daemon_get_on_ac_local:
*
* As soon as _any_ ac supply goes online, this is true
**/
static gboolean
up_daemon_get_on_ac_local (UpDaemon *daemon)
{
guint i;
gboolean ret;
gboolean result = FALSE;
gboolean online;
UpDevice *device;
GPtrArray *array;
/* ask each device */
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
ret = up_device_get_online (device, &online);
if (ret && online) {
result = TRUE;
break;
}
}
g_ptr_array_unref (array);
return result;
}
/**
* up_daemon_set_powersave:
**/
static gboolean
up_daemon_set_powersave (UpDaemon *daemon, gboolean powersave)
{
gboolean ret = FALSE;
const gchar *command;
GError *error = NULL;
/* run script */
command = up_backend_get_powersave_command (daemon->priv->backend, powersave);
if (command == NULL) {
g_warning ("no powersave command set");
goto out;
}
g_debug ("excuting command: %s", command);
ret = g_spawn_command_line_async (command, &error);
if (!ret) {
g_warning ("failed to run script: %s", error->message);
g_error_free (error);
goto out;
}
out:
return ret;
}
/**
* up_daemon_refresh_battery_devices:
**/
static gboolean
up_daemon_refresh_battery_devices (UpDaemon *daemon)
{
guint i;
GPtrArray *array;
UpDevice *device;
UpDeviceKind type;
/* refresh all devices in array */
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
/* only refresh battery devices */
g_object_get (device,
"type", &type,
NULL);
if (type == UP_DEVICE_KIND_BATTERY)
up_device_refresh_internal (device);
}
g_ptr_array_unref (array);
return TRUE;
}
/**
* up_daemon_enumerate_devices:
**/
gboolean
up_daemon_enumerate_devices (UpDaemon *daemon, DBusGMethodInvocation *context)
{
guint i;
GPtrArray *array;
GPtrArray *object_paths;
UpDevice *device;
/* build a pointer array of the object paths */
object_paths = g_ptr_array_new_with_free_func (g_free);
array = up_device_list_get_array (daemon->priv->power_devices);
for (i=0; i<array->len; i++) {
device = (UpDevice *) g_ptr_array_index (array, i);
g_ptr_array_add (object_paths, g_strdup (up_device_get_object_path (device)));
}
g_ptr_array_unref (array);
/* return it on the bus */
dbus_g_method_return (context, object_paths);
/* free */
g_ptr_array_unref (object_paths);
return TRUE;
}
/**
* up_daemon_about_to_sleep:
**/
gboolean
up_daemon_about_to_sleep (UpDaemon *daemon,
const gchar *sleep_kind,
DBusGMethodInvocation *context)
{
PolkitSubject *subject = NULL;
GError *error;
UpDaemonPrivate *priv = daemon->priv;
/* already requested */
if (priv->about_to_sleep_id != 0) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Sleep has already been requested and is pending");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
subject = up_polkit_get_subject (priv->polkit, context);
if (subject == NULL)
goto out;
/* TODO: use another PolicyKit context? */
if (!up_polkit_check_auth (priv->polkit, subject, "org.freedesktop.upower.suspend", context))
goto out;
/* we've told the clients we're going down */
g_debug ("emitting sleeping");
g_signal_emit (daemon, signals[SIGNAL_SLEEPING], 0);
g_signal_emit (daemon, signals[SIGNAL_NOTIFY_SLEEP], 0,
sleep_kind);
g_timer_start (priv->about_to_sleep_timer);
daemon->priv->sent_sleeping_signal = TRUE;
dbus_g_method_return (context, NULL);
out:
if (subject != NULL)
g_object_unref (subject);
return TRUE;
}
/* temp object for deferred callback */
typedef struct {
UpDaemon *daemon;
DBusGMethodInvocation *context;
gchar *command;
} UpDaemonDeferredSleep;
/**
* up_daemon_deferred_sleep_cb:
**/
static gboolean
up_daemon_deferred_sleep_cb (UpDaemonDeferredSleep *sleep)
{
GError *error;
GError *error_local = NULL;
gchar *stdout = NULL;
gchar *stderr = NULL;
gboolean ret;
UpDaemon *daemon = sleep->daemon;
UpDaemonPrivate *priv = daemon->priv;
/* run the command */
ret = g_spawn_command_line_sync (sleep->command, &stdout, &stderr, NULL, &error_local);
if (!ret) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Failed to spawn: %s, stdout:%s, stderr:%s", error_local->message, stdout, stderr);
g_error_free (error_local);
dbus_g_method_return_error (sleep->context, error);
g_error_free (error);
goto out;
}
/* emit signal for session components */
g_debug ("emitting resuming");
g_signal_emit (daemon, signals[SIGNAL_RESUMING], 0);
g_signal_emit (daemon, signals[SIGNAL_NOTIFY_RESUME], 0,
priv->sleep_kind);
/* reset the about-to-sleep logic */
g_timer_reset (priv->about_to_sleep_timer);
g_timer_stop (priv->about_to_sleep_timer);
/* actually return from the DBus call now */
dbus_g_method_return (sleep->context, NULL);
out:
/* clear timer */
priv->about_to_sleep_id = 0;
priv->sent_sleeping_signal = FALSE;
g_free (stdout);
g_free (stderr);
/* delete temp object */
g_object_unref (sleep->daemon);
g_free (sleep->command);
g_free (sleep);
return FALSE;
}
/**
* up_daemon_deferred_sleep:
**/
static void
up_daemon_deferred_sleep (UpDaemon *daemon, const gchar *command, DBusGMethodInvocation *context)
{
UpDaemonDeferredSleep *sleep;
UpDaemonPrivate *priv = daemon->priv;
gfloat elapsed;
/* create callback object */
sleep = g_new0 (UpDaemonDeferredSleep, 1);
sleep->daemon = g_object_ref (daemon);
sleep->context = context;
sleep->command = g_strdup (command);
/* we didn't use AboutToSleep() so send the signal for clients now */
if (!priv->sent_sleeping_signal) {
g_debug ("no AboutToSleep(), so emitting ::Sleeping()");
g_signal_emit (daemon, signals[SIGNAL_SLEEPING], 0);
g_signal_emit (daemon, signals[SIGNAL_NOTIFY_SLEEP], 0,
priv->sleep_kind);
priv->about_to_sleep_id = g_timeout_add (priv->conf_sleep_timeout,
(GSourceFunc) up_daemon_deferred_sleep_cb, sleep);
#if GLIB_CHECK_VERSION(2,25,8)
g_source_set_name_by_id (priv->about_to_sleep_id, "[UpDaemon] about-to-sleep no signal");
#endif
return;
}
/* about to sleep */
elapsed = 1000.0f * g_timer_elapsed (priv->about_to_sleep_timer, NULL);
g_debug ("between AboutToSleep() and %s was %fms", sleep->command, elapsed);
if (elapsed < priv->conf_sleep_timeout) {
/* we have to wait for the difference in time */
priv->about_to_sleep_id = g_timeout_add (priv->conf_sleep_timeout - elapsed,
(GSourceFunc) up_daemon_deferred_sleep_cb, sleep);
#if GLIB_CHECK_VERSION(2,25,8)
g_source_set_name_by_id (priv->about_to_sleep_id, "[UpDaemon] about-to-sleep less");
#endif
} else {
/* we can do this straight away */
priv->about_to_sleep_id = g_idle_add ((GSourceFunc) up_daemon_deferred_sleep_cb, sleep);
#if GLIB_CHECK_VERSION(2,25,8)
g_source_set_name_by_id (priv->about_to_sleep_id, "[UpDaemon] about-to-sleep more");
#endif
}
}
/**
* up_daemon_suspend:
**/
gboolean
up_daemon_suspend (UpDaemon *daemon, DBusGMethodInvocation *context)
{
GError *error;
PolkitSubject *subject = NULL;
const gchar *command;
UpDaemonPrivate *priv = daemon->priv;
/* no kernel support */
if (!priv->kernel_can_suspend) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"No kernel support");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
subject = up_polkit_get_subject (priv->polkit, context);
if (subject == NULL)
goto out;
if (!up_polkit_check_auth (priv->polkit, subject, "org.freedesktop.upower.suspend", context))
goto out;
/* already requested */
if (priv->about_to_sleep_id != 0) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Sleep has already been requested and is pending");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
/* do this deferred action */
priv->sleep_kind = "suspend";
command = up_backend_get_suspend_command (priv->backend);
up_daemon_deferred_sleep (daemon, command, context);
out:
if (subject != NULL)
g_object_unref (subject);
return TRUE;
}
/**
* up_daemon_suspend_allowed:
**/
gboolean
up_daemon_suspend_allowed (UpDaemon *daemon, DBusGMethodInvocation *context)
{
gboolean ret;
PolkitSubject *subject = NULL;
UpDaemonPrivate *priv = daemon->priv;
GError *error;
subject = up_polkit_get_subject (priv->polkit, context);
if (subject == NULL)
goto out;
error = NULL;
ret = up_polkit_is_allowed (priv->polkit, subject, "org.freedesktop.upower.suspend", &error);
if (error) {
dbus_g_method_return_error (context, error);
g_error_free (error);
}
else {
dbus_g_method_return (context, ret);
}
out:
if (subject != NULL)
g_object_unref (subject);
return TRUE;
}
/**
* up_daemon_check_hibernate_swap:
*
* Check current memory usage whether we have enough swap space for
* hibernate.
**/
static gboolean
up_daemon_check_hibernate_swap (UpDaemon *daemon)
{
gfloat waterline;
if (daemon->priv->kernel_can_hibernate) {
waterline = up_backend_get_used_swap (daemon->priv->backend);
if (waterline < UP_DAEMON_SWAP_WATERLINE) {
g_debug ("enough swap to for hibernate");
return TRUE;
} else {
g_debug ("not enough swap to hibernate");
return FALSE;
}
}
return FALSE;
}
/**
* up_daemon_hibernate:
**/
gboolean
up_daemon_hibernate (UpDaemon *daemon, DBusGMethodInvocation *context)
{
GError *error;
PolkitSubject *subject = NULL;
const gchar *command;
UpDaemonPrivate *priv = daemon->priv;
/* no kernel support */
if (!priv->kernel_can_hibernate) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"No kernel support");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
/* enough swap? */
if (!up_daemon_check_hibernate_swap (daemon)) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Not enough swap space");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
/* encrypted swap and no override? */
if (priv->hibernate_has_encrypted_swap &&
!priv->conf_allow_hibernate_encrypted_swap) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Swap space is encrypted, use AllowHibernateEncryptedSwap to override");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
subject = up_polkit_get_subject (priv->polkit, context);
if (subject == NULL)
goto out;
if (!up_polkit_check_auth (priv->polkit, subject, "org.freedesktop.upower.hibernate", context))
goto out;
/* already requested */
if (priv->about_to_sleep_id != 0) {
error = g_error_new (UP_DAEMON_ERROR,
UP_DAEMON_ERROR_GENERAL,
"Sleep has already been requested and is pending");
dbus_g_method_return_error (context, error);
g_error_free (error);
goto out;
}
/* do this deferred action */
priv->sleep_kind = "hibernate";
command = up_backend_get_hibernate_command (priv->backend);
up_daemon_deferred_sleep (daemon, command, context);
out:
if (subject != NULL)
g_object_unref (subject);
return TRUE;
}
/**
* up_daemon_hibernate_allowed:
**/
gboolean
up_daemon_hibernate_allowed (UpDaemon *daemon, DBusGMethodInvocation *context)
{
gboolean ret;
PolkitSubject *subject = NULL;
UpDaemonPrivate *priv = daemon->priv;
GError *error;
subject = up_polkit_get_subject (priv->polkit, context);
if (subject == NULL)
goto out;
error = NULL;
ret = up_polkit_is_allowed (priv->polkit, subject, "org.freedesktop.upower.hibernate", &error);
if (error) {
dbus_g_method_return_error (context, error);
g_error_free (error);
}
else {
dbus_g_method_return (context, ret);
}
out:
if (subject != NULL)
g_object_unref (subject);
return TRUE;
}
/**
* up_daemon_register_power_daemon:
**/
static gboolean
up_daemon_register_power_daemon (UpDaemon *daemon)
{
GError *error = NULL;
gboolean ret = FALSE;
UpDaemonPrivate *priv = daemon->priv;
priv->connection = dbus_g_bus_get (up_daemon_get_bus_type (), &error);
if (priv->connection == NULL) {
if (error != NULL) {
g_critical ("error getting system bus: %s", error->message);
g_error_free (error);
}
goto out;
}
/* connect to DBUS */
priv->proxy = dbus_g_proxy_new_for_name (priv->connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
/* register GObject */
dbus_g_connection_register_g_object (priv->connection,
"/org/freedesktop/UPower",
G_OBJECT (daemon));
/* success */
ret = TRUE;
out:
return ret;
}
/**
* up_daemon_startup:
**/
gboolean
up_daemon_startup (UpDaemon *daemon)
{
gboolean ret;
gboolean on_battery;
gboolean on_low_battery;
UpDaemonPrivate *priv = daemon->priv;
/* register on bus */
ret = up_daemon_register_power_daemon (daemon);
if (!ret) {
g_warning ("failed to register");
goto out;
}
/* stop signals and callbacks */
g_debug ("daemon now coldplug");
g_object_freeze_notify (G_OBJECT(daemon));
priv->during_coldplug = TRUE;
/* coldplug backend backend */
ret = up_backend_coldplug (priv->backend, daemon);
if (!ret) {
g_warning ("failed to coldplug backend");
goto out;
}
/* get battery state */
on_battery = (up_daemon_get_on_battery_local (daemon) &&
!up_daemon_get_on_ac_local (daemon));
on_low_battery = up_daemon_get_on_low_battery_local (daemon);
up_daemon_set_on_battery (daemon, on_battery);
up_daemon_set_on_low_battery (daemon, on_low_battery);
/* start signals and callbacks */
g_object_thaw_notify (G_OBJECT(daemon));
priv->during_coldplug = FALSE;
g_debug ("daemon now not coldplug");
/* set power policy */
up_daemon_set_powersave (daemon, priv->on_battery);
out:
return ret;
}
/**
* up_daemon_get_device_list:
**/
UpDeviceList *
up_daemon_get_device_list (UpDaemon *daemon)
{
return g_object_ref (daemon->priv->power_devices);
}
/**
* up_daemon_set_lid_is_closed:
**/
void
up_daemon_set_lid_is_closed (UpDaemon *daemon, gboolean lid_is_closed)
{
UpDaemonPrivate *priv = daemon->priv;
/* check if we are ignoring the lid */
if (up_config_get_boolean (priv->config, "IgnoreLid")) {
g_debug ("ignoring lid state");
return;
}
g_debug ("lid_is_closed = %s", lid_is_closed ? "yes" : "no");
priv->lid_is_closed = lid_is_closed;
g_object_notify (G_OBJECT (daemon), "lid-is-closed");
}
/**
* up_daemon_set_lid_force_sleep:
**/
void
up_daemon_set_lid_force_sleep (UpDaemon *daemon, gboolean lid_force_sleep)
{
UpDaemonPrivate *priv = daemon->priv;
g_debug ("lid_force_sleep = %s", lid_force_sleep ? "yes" : "no");
priv->lid_force_sleep = lid_force_sleep;
g_object_notify (G_OBJECT (daemon), "lid-enforce-sleep");
}
/**
* up_daemon_set_lid_is_present:
**/
void
up_daemon_set_lid_is_present (UpDaemon *daemon, gboolean lid_is_present)
{
UpDaemonPrivate *priv = daemon->priv;
/* check if we are ignoring the lid */
if (up_config_get_boolean (priv->config, "IgnoreLid")) {
g_debug ("ignoring lid state");
return;
}
g_debug ("lid_is_present = %s", lid_is_present ? "yes" : "no");
priv->lid_is_present = lid_is_present;
g_object_notify (G_OBJECT (daemon), "lid-is-present");
}
/**
* up_daemon_set_is_docked:
**/
void
up_daemon_set_is_docked (UpDaemon *daemon, gboolean is_docked)
{
UpDaemonPrivate *priv = daemon->priv;
g_debug ("is_docked = %s", is_docked ? "yes" : "no");
priv->is_docked = is_docked;
g_object_notify (G_OBJECT (daemon), "is-docked");
}
/**
* up_daemon_set_on_battery:
**/
void
up_daemon_set_on_battery (UpDaemon *daemon, gboolean on_battery)
{
UpDaemonPrivate *priv = daemon->priv;
g_debug ("on_battery = %s", on_battery ? "yes" : "no");
priv->on_battery = on_battery;
g_object_notify (G_OBJECT (daemon), "on-battery");
}
/**
* up_daemon_set_on_low_battery:
**/
void
up_daemon_set_on_low_battery (UpDaemon *daemon, gboolean on_low_battery)
{
UpDaemonPrivate *priv = daemon->priv;
g_debug ("on_low_battery = %s", on_low_battery ? "yes" : "no");
priv->on_low_battery = on_low_battery;
g_object_notify (G_OBJECT (daemon), "on-low-battery");
}
/**
* up_daemon_refresh_battery_devices_cb:
**/
static gboolean
up_daemon_refresh_battery_devices_cb (UpDaemon *daemon)
{
UpDaemonPrivate *priv = daemon->priv;
/* no more left to do? */
if (priv->battery_poll_count-- == 0) {
priv->battery_poll_id = 0;
return FALSE;
}
g_debug ("doing the delayed refresh (%i)", priv->battery_poll_count);
up_daemon_refresh_battery_devices (daemon);
/* keep going until none left to do */
return TRUE;
}
/**
* up_daemon_poll_battery_devices_for_a_little_bit:
**/
static void
up_daemon_poll_battery_devices_for_a_little_bit (UpDaemon *daemon)
{
UpDaemonPrivate *priv = daemon->priv;
priv->battery_poll_count = UP_DAEMON_POLL_BATTERY_NUMBER_TIMES;
/* already polling */
if (priv->battery_poll_id != 0)
return;
priv->battery_poll_id =
g_timeout_add_seconds (UP_DAEMON_ON_BATTERY_REFRESH_DEVICES_DELAY,
(GSourceFunc) up_daemon_refresh_battery_devices_cb, daemon);
#if GLIB_CHECK_VERSION(2,25,8)
g_source_set_name_by_id (priv->battery_poll_id, "[UpDaemon] poll batteries for AC event");
#endif
}
/**
* up_daemon_device_changed_cb:
**/
static void
up_daemon_device_changed_cb (UpDevice *device, UpDaemon *daemon)
{
const gchar *object_path;
UpDeviceKind type;
gboolean ret;
UpDaemonPrivate *priv = daemon->priv;
g_return_if_fail (UP_IS_DAEMON (daemon));
g_return_if_fail (UP_IS_DEVICE (device));
/* refresh battery devices when AC state changes */
g_object_get (device,
"type", &type,
NULL);
if (type == UP_DEVICE_KIND_LINE_POWER) {
/* refresh now, and again in a little while */
up_daemon_refresh_battery_devices (daemon);
up_daemon_poll_battery_devices_for_a_little_bit (daemon);
}
/* second, check if the on_battery and on_low_battery state has changed */
ret = (up_daemon_get_on_battery_local (daemon) && !up_daemon_get_on_ac_local (daemon));
if (ret != priv->on_battery) {
up_daemon_set_on_battery (daemon, ret);
/* set power policy */
up_daemon_set_powersave (daemon, ret);
}
ret = up_daemon_get_on_low_battery_local (daemon);
if (ret != priv->on_low_battery)
up_daemon_set_on_low_battery (daemon, ret);
/* emit */
if (!priv->during_coldplug) {
object_path = up_device_get_object_path (device);
g_debug ("emitting device-changed: %s", object_path);
/* don't crash the session */
if (object_path == NULL) {
g_warning ("INTERNAL STATE CORRUPT: not sending NULL, device:%p", device);
return;
}
g_signal_emit (daemon, signals[SIGNAL_DEVICE_CHANGED], 0, object_path);
}
}
/**
* up_daemon_device_added_cb:
**/
static void
up_daemon_device_added_cb (UpBackend *backend, GObject *native, UpDevice *device, UpDaemon *daemon)
{
UpDeviceKind type;
const gchar *object_path;
UpDaemonPrivate *priv = daemon->priv;
g_return_if_fail (UP_IS_DAEMON (daemon));
g_return_if_fail (UP_IS_DEVICE (device));
g_return_if_fail (G_IS_OBJECT (native));
/* add to device list */
up_device_list_insert (priv->power_devices, native, G_OBJECT (device));
/* connect, so we get changes */
g_signal_connect (device, "changed",
G_CALLBACK (up_daemon_device_changed_cb), daemon);
/* refresh after a short delay */
g_object_get (device,
"type", &type,
NULL);
if (type == UP_DEVICE_KIND_BATTERY)
up_daemon_poll_battery_devices_for_a_little_bit (daemon);
/* emit */
if (!priv->during_coldplug) {
object_path = up_device_get_object_path (device);
g_debug ("emitting added: %s (during coldplug %i)", object_path, priv->during_coldplug);
/* don't crash the session */
if (object_path == NULL) {
g_warning ("INTERNAL STATE CORRUPT: not sending NULL, native:%p, device:%p", native, device);
return;
}
g_signal_emit (daemon, signals[SIGNAL_DEVICE_ADDED], 0, object_path);
}
}
/**
* up_daemon_device_removed_cb:
**/
static void
up_daemon_device_removed_cb (UpBackend *backend, GObject *native, UpDevice *device, UpDaemon *daemon)
{
UpDeviceKind type;
const gchar *object_path;
UpDaemonPrivate *priv = daemon->priv;
g_return_if_fail (UP_IS_DAEMON (daemon));
g_return_if_fail (UP_IS_DEVICE (device));
g_return_if_fail (G_IS_OBJECT (native));
/* remove from list */
up_device_list_remove (priv->power_devices, G_OBJECT(device));
/* refresh after a short delay */
g_object_get (device,
"type", &type,
NULL);
if (type == UP_DEVICE_KIND_BATTERY)
up_daemon_poll_battery_devices_for_a_little_bit (daemon);
/* emit */
if (!priv->during_coldplug) {
object_path = up_device_get_object_path (device);
g_debug ("emitting device-removed: %s", object_path);
/* don't crash the session */
if (object_path == NULL) {
g_warning ("INTERNAL STATE CORRUPT: not sending NULL, native:%p, device:%p", native, device);
return;
}
g_signal_emit (daemon, signals[SIGNAL_DEVICE_REMOVED], 0, object_path);
}
/* finalise the object */
g_object_unref (device);
}
/**
* up_daemon_properties_changed_cb:
**/
static void
up_daemon_properties_changed_cb (GObject *object, GParamSpec *pspec, UpDaemon *daemon)
{
g_return_if_fail (UP_IS_DAEMON (daemon));
/* emit */
if (!daemon->priv->during_coldplug) {
g_debug ("emitting changed");
g_signal_emit (daemon, signals[SIGNAL_CHANGED], 0);
}
}
/**
* up_daemon_init:
**/
static void
up_daemon_init (UpDaemon *daemon)
{
gboolean ret;
GError *error = NULL;
GKeyFile *file;
const gchar *filename_self_test;
gchar *filename;
daemon->priv = UP_DAEMON_GET_PRIVATE (daemon);
daemon->priv->polkit = up_polkit_new ();
daemon->priv->config = up_config_new ();
daemon->priv->lid_is_present = FALSE;
daemon->priv->is_docked = FALSE;
daemon->priv->lid_is_closed = FALSE;
daemon->priv->kernel_can_suspend = FALSE;
daemon->priv->kernel_can_hibernate = FALSE;
daemon->priv->hibernate_has_encrypted_swap = FALSE;
daemon->priv->power_devices = up_device_list_new ();
daemon->priv->on_battery = FALSE;
daemon->priv->on_low_battery = FALSE;
daemon->priv->during_coldplug = FALSE;
daemon->priv->sent_sleeping_signal = FALSE;
daemon->priv->battery_poll_id = 0;
daemon->priv->battery_poll_count = 0;
daemon->priv->about_to_sleep_id = 0;
daemon->priv->conf_sleep_timeout = 1000;
daemon->priv->conf_allow_hibernate_encrypted_swap = FALSE;
/* load some values from the config file */
file = g_key_file_new ();
filename_self_test = g_getenv ("UPOWER_CONF_FILE_NAME");
if (filename_self_test != NULL) {
g_debug ("using %s as the self test conf file", filename_self_test);
filename = g_strdup (filename_self_test);
} else {
filename = g_build_filename (PACKAGE_SYSCONF_DIR,"UPower", "UPower.conf", NULL);
}
ret = g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error);
if (ret) {
daemon->priv->conf_sleep_timeout =
g_key_file_get_integer (file, "UPower", "SleepTimeout", NULL);
daemon->priv->conf_allow_hibernate_encrypted_swap =
g_key_file_get_boolean (file, "UPower", "AllowHibernateEncryptedSwap", NULL);
} else {
g_warning ("failed to load config file %s: %s", filename, error->message);
g_error_free (error);
}
g_key_file_free (file);
g_free (filename);
daemon->priv->backend = up_backend_new ();
g_signal_connect (daemon->priv->backend, "device-added",
G_CALLBACK (up_daemon_device_added_cb), daemon);
g_signal_connect (daemon->priv->backend, "device-removed",
G_CALLBACK (up_daemon_device_removed_cb), daemon);
/* use a timer for the about-to-sleep logic */
daemon->priv->about_to_sleep_timer = g_timer_new ();
g_timer_stop (daemon->priv->about_to_sleep_timer);
/* watch when these properties change */
g_signal_connect (daemon, "notify::lid-is-present",
G_CALLBACK (up_daemon_properties_changed_cb), daemon);
g_signal_connect (daemon, "notify::lid-is-closed",
G_CALLBACK (up_daemon_properties_changed_cb), daemon);
g_signal_connect (daemon, "notify::on-battery",
G_CALLBACK (up_daemon_properties_changed_cb), daemon);
g_signal_connect (daemon, "notify::on-low-battery",
G_CALLBACK (up_daemon_properties_changed_cb), daemon);
/* check if we have support */
daemon->priv->kernel_can_suspend = up_backend_kernel_can_suspend (daemon->priv->backend);
daemon->priv->kernel_can_hibernate = up_backend_kernel_can_hibernate (daemon->priv->backend);
/* is the swap usable? */
if (daemon->priv->kernel_can_hibernate)
daemon->priv->hibernate_has_encrypted_swap = up_backend_has_encrypted_swap (daemon->priv->backend);
}
/**
* up_daemon_error_quark:
**/
GQuark
up_daemon_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0)
ret = g_quark_from_static_string ("up_daemon_error");
return ret;
}
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
/**
* up_daemon_error_get_type:
**/
GType
up_daemon_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
ENUM_ENTRY (UP_DAEMON_ERROR_GENERAL, "GeneralError"),
ENUM_ENTRY (UP_DAEMON_ERROR_NOT_SUPPORTED, "NotSupported"),
ENUM_ENTRY (UP_DAEMON_ERROR_NO_SUCH_DEVICE, "NoSuchDevice"),
{ 0, 0, 0 }
};
g_assert (UP_DAEMON_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
etype = g_enum_register_static ("UpDaemonError", values);
}
return etype;
}
/**
* up_daemon_get_property:
**/
static void
up_daemon_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
UpDaemon *daemon = UP_DAEMON (object);
UpDaemonPrivate *priv = daemon->priv;
switch (prop_id) {
case PROP_DAEMON_VERSION:
g_value_set_string (value, PACKAGE_VERSION);
break;
case PROP_CAN_SUSPEND:
g_value_set_boolean (value, priv->kernel_can_suspend);
break;
case PROP_CAN_HIBERNATE:
g_value_set_boolean (value, (priv->kernel_can_hibernate &&
up_daemon_check_hibernate_swap (daemon) &&
(!priv->hibernate_has_encrypted_swap ||
priv->conf_allow_hibernate_encrypted_swap)));
break;
case PROP_ON_BATTERY:
g_value_set_boolean (value, priv->on_battery);
break;
case PROP_ON_LOW_BATTERY:
g_value_set_boolean (value, priv->on_battery && priv->on_low_battery);
break;
case PROP_LID_IS_CLOSED:
g_value_set_boolean (value, priv->lid_is_closed);
break;
case PROP_LID_IS_PRESENT:
g_value_set_boolean (value, priv->lid_is_present);
break;
case PROP_LID_FORCE_SLEEP:
g_value_set_boolean (value, priv->lid_force_sleep);
break;
case PROP_IS_DOCKED:
g_value_set_boolean (value, priv->is_docked);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* up_daemon_set_property:
**/
static void
up_daemon_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* up_daemon_class_init:
**/
static void
up_daemon_class_init (UpDaemonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = up_daemon_finalize;
object_class->get_property = up_daemon_get_property;
object_class->set_property = up_daemon_set_property;
g_type_class_add_private (klass, sizeof (UpDaemonPrivate));
signals[SIGNAL_DEVICE_ADDED] =
g_signal_new ("device-added",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SIGNAL_DEVICE_REMOVED] =
g_signal_new ("device-removed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SIGNAL_DEVICE_CHANGED] =
g_signal_new ("device-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SIGNAL_CHANGED] =
g_signal_new ("changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_SLEEPING] =
g_signal_new ("sleeping",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_NOTIFY_SLEEP] =
g_signal_new ("notify-sleep",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SIGNAL_RESUMING] =
g_signal_new ("resuming",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_NOTIFY_RESUME] =
g_signal_new ("notify-resume",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0, NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
g_object_class_install_property (object_class,
PROP_DAEMON_VERSION,
g_param_spec_string ("daemon-version",
"Daemon Version",
"The version of the running daemon",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_LID_IS_PRESENT,
g_param_spec_boolean ("lid-is-present",
"Is a laptop",
"If this computer is probably a laptop",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_LID_FORCE_SLEEP,
g_param_spec_boolean ("lid-force-sleep",
"Enforce sleep on lid close",
"If this computer has to sleep on lid close",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_IS_DOCKED,
g_param_spec_boolean ("is-docked",
"Is docked",
"If this computer is docked",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_CAN_SUSPEND,
g_param_spec_boolean ("can-suspend",
"Can Suspend",
"Whether the system can suspend",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_CAN_HIBERNATE,
g_param_spec_boolean ("can-hibernate",
"Can Hibernate",
"Whether the system can hibernate",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_ON_BATTERY,
g_param_spec_boolean ("on-battery",
"On Battery",
"Whether the system is running on battery",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_ON_LOW_BATTERY,
g_param_spec_boolean ("on-low-battery",
"On Low Battery",
"Whether the system is running on battery and if the battery is critically low",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_LID_IS_CLOSED,
g_param_spec_boolean ("lid-is-closed",
"Laptop lid is closed",
"If the laptop lid is closed",
FALSE,
G_PARAM_READABLE));
dbus_g_object_type_install_info (UP_TYPE_DAEMON, &dbus_glib_up_daemon_object_info);
dbus_g_error_domain_register (UP_DAEMON_ERROR, NULL, UP_DAEMON_TYPE_ERROR);
}
/**
* up_daemon_finalize:
**/
static void
up_daemon_finalize (GObject *object)
{
UpDaemon *daemon = UP_DAEMON (object);
UpDaemonPrivate *priv = daemon->priv;
if (priv->battery_poll_id != 0)
g_source_remove (priv->battery_poll_id);
if (priv->proxy != NULL)
g_object_unref (priv->proxy);
if (priv->connection != NULL)
dbus_g_connection_unref (priv->connection);
g_object_unref (priv->power_devices);
g_object_unref (priv->polkit);
g_object_unref (priv->config);
g_object_unref (priv->backend);
g_timer_destroy (priv->about_to_sleep_timer);
G_OBJECT_CLASS (up_daemon_parent_class)->finalize (object);
}
/**
* up_daemon_new:
**/
UpDaemon *
up_daemon_new (void)
{
UpDaemon *daemon;
daemon = UP_DAEMON (g_object_new (UP_TYPE_DAEMON, NULL));
return daemon;
}