upower/src/up-daemon.c
Landry Breuil 19790b99a2 Add boolean RunPowersaveCommand to UPower.conf
Defaulting to true, it controls whether we want the powersave commands
to be run when running on battery/plugging ac.

Signed-off-by: Richard Hughes <richard@hughsie.com>
2012-01-06 16:40:09 +00:00

1432 lines
38 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;
gboolean conf_run_powersave_command;
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 */
if (priv->conf_run_powersave_command)
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 */
if (priv->conf_run_powersave_command)
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;
daemon->priv->conf_run_powersave_command = TRUE;
/* 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);
daemon->priv->conf_run_powersave_command =
g_key_file_get_boolean (file, "UPower", "RunPowersaveCommand", 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;
}