Use systemd for suspend and hibernate

When running under systemd, call into systemd for suspend and
hibernate instead of pm-utils. To capture resume events, install
a small script that gets executed by systemd after resume and
sends a dbus signal back to upower.

To make this work, the upower backends gain a new signal, ::resuming,
that they can optionally emit to signal that a resume happened.
Backends opt in to this by returning TRUE from up_backend_emits_resuming().
In this case, upower doesn't assume the sleep command to block until
resume, but instead waits for the ::resuming signal from the backend.

The only backend that uses this mechanism is the linux backend when
built with systemd support.

Signed-off-by: Richard Hughes <richard@hughsie.com>
This commit is contained in:
Matthias Clasen 2012-07-11 15:06:04 +01:00 committed by Richard Hughes
parent 7bad068a2d
commit 44189f8129
10 changed files with 194 additions and 32 deletions

View file

@ -176,6 +176,55 @@ fi
PKG_CHECK_MODULES(GIO, [gio-2.0 >= 2.16.1])
dnl ====================================================================
dnl Check for systemd
dnl ====================================================================
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [Use systemd]),
[enable_systemd=$enableval],
[enable_systemd=auto])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-daemon],
[have_systemd=yes], [have_systemd=no])
AC_MSG_CHECKING([whether to use systemd])
if test x$enable_systemd = xauto ; then
if test x$have_systemd = xno ; then
enable_systemd=no
else
enable_systemd=yes
fi
fi
AC_MSG_RESULT($enable_systemd)
if test x$enable_systemd = xyes; then
if test x$have_systemd = xno; then
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
fi
AC_DEFINE(HAVE_SYSTEMD, 1, [Define if systemd is used for session tracking])
fi
AC_SUBST(SYSTEMD_CFLAGS)
AC_SUBST(SYSTEMD_LIBS)
AM_CONDITIONAL(HAVE_SYSTEMD, [test "$with_systemd" = "yes"], [Using systemd])
AC_ARG_WITH([systemdutildir],
AS_HELP_STRING([--with-systemdutildir=DIR], [Directory for systemd utilities]),
[],
[with_systemdutildir=$($PKG_CONFIG --variable=systemdutildir systemd)])
AC_SUBST([systemdutildir], [$with_systemdutildir])
AM_CONDITIONAL(HAVE_SYSTEMDUTILDIR, [test -n "$with_systemdutildir"])
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[],
[with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
AM_CONDITIONAL(HAVE_SYSTEMDSYSTEMUNITDIR, [test -n "$with_systemdsystemunitdir"])
dnl ---------------------------------------------------------------------------
dnl - Compile time default choice of backend
dnl ---------------------------------------------------------------------------
@ -210,14 +259,6 @@ fi
AM_CONDITIONAL(HAVE_IDEVICE, [test x$have_idevice = xyes])
# systemd
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[],
[with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir"])
# export to Makefile.am
AM_CONDITIONAL(BACKEND_TYPE_DUMMY, [test x$with_backend = xdummy])
AM_CONDITIONAL(BACKEND_TYPE_LINUX, [test x$with_backend = xlinux])

View file

@ -122,7 +122,13 @@ upowerd_LDADD += \
linux/libupshared.la \
$(USB_LIBS) \
$(GUDEV_LIBS) \
$(IDEVICE_LIBS)
$(IDEVICE_LIBS) \
$(SYSTEMD_LIBS)
if HAVE_SYSTEMDUTILDIR
systemsleepdir = $(systemdutildir)/system-sleep
systemsleep_SCRIPTS = notify-upower.sh
endif
if UP_BUILD_TESTS
DBUS_LAUNCH=$(shell which dbus-launch)
@ -196,7 +202,7 @@ dbusconf_DATA = $(dbusconf_in_files:.conf.in=.conf)
$(dbusconf_DATA): $(dbusconf_in_files) Makefile
cp $< $@
if HAVE_SYSTEMD
if HAVE_SYSTEMDSYSTEMUNITDIR
systemdservicedir = $(systemdsystemunitdir)
systemdservice_in_files = upower.service.in
systemdservice_DATA = $(systemdservice_in_files:.service.in=.service)
@ -223,6 +229,8 @@ EXTRA_DIST = \
org.freedesktop.UPower.KbdBacklight.xml \
org.freedesktop.UPower.Wakeups.xml \
up-marshal.list \
$(systemsleep_SCRIPTS) \
$(systemdservice_in_files) \
$(dbusservice_in_files) \
$(dbusconf_in_files)

View file

@ -297,3 +297,9 @@ up_backend_get_powersave_command (UpBackend *backend, gboolean powersave)
{
return "/bin/true";
}
gboolean
up_backend_emits_resuming (UpBackend *backend)
{
return FALSE;
}

View file

@ -326,6 +326,12 @@ up_backend_get_hibernate_command (UpBackend *backend)
return UP_BACKEND_HIBERNATE_COMMAND;
}
gboolean
up_backend_emits_resuming (UpBackend *backend)
{
return FALSE;
}
/**
* up_backend_kernel_can_suspend:
**/

View file

@ -12,7 +12,8 @@ INCLUDES = \
$(GUDEV_CFLAGS) \
$(POLKIT_CFLAGS) \
$(GLIB_CFLAGS) \
$(IDEVICE_CFLAGS)
$(IDEVICE_CFLAGS) \
$(SYSTEMD_CFLAGS)
if BACKEND_TYPE_LINUX
noinst_LTLIBRARIES = libupshared.la

View file

@ -45,6 +45,17 @@
#include "up-device-idevice.h"
#endif /* HAVE_IDEVICE */
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#define SD_HIBERNATE_COMMAND "gdbus call --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1 --method org.freedesktop.login1.Manager.Hibernate 'true'"
#define SD_SUSPEND_COMMAND "gdbus call --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1 --method org.freedesktop.login1.Manager.Suspend 'true'"
#endif
static void up_backend_class_init (UpBackendClass *klass);
static void up_backend_init (UpBackend *backend);
static void up_backend_finalize (GObject *object);
@ -59,11 +70,13 @@ struct UpBackendPrivate
UpDeviceList *managed_devices;
UpDock *dock;
UpConfig *config;
DBusConnection *connection;
};
enum {
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_RESUMING,
SIGNAL_LAST
};
@ -564,6 +577,11 @@ out:
const gchar *
up_backend_get_suspend_command (UpBackend *backend)
{
#ifdef HAVE_SYSTEMD
if (sd_booted ())
return SD_SUSPEND_COMMAND;
else
#endif
return UP_BACKEND_SUSPEND_COMMAND;
}
@ -573,9 +591,24 @@ up_backend_get_suspend_command (UpBackend *backend)
const gchar *
up_backend_get_hibernate_command (UpBackend *backend)
{
#ifdef HAVE_SYSTEMD
if (sd_booted ())
return SD_HIBERNATE_COMMAND;
else
#endif
return UP_BACKEND_HIBERNATE_COMMAND;
}
gboolean
up_backend_emits_resuming (UpBackend *backend)
{
#ifdef HAVE_SYSTEMD
return TRUE;
#else
return FALSE;
#endif
}
/**
* up_backend_get_powersave_command:
**/
@ -609,10 +642,32 @@ up_backend_class_init (UpBackendClass *klass)
G_STRUCT_OFFSET (UpBackendClass, device_removed),
NULL, NULL, up_marshal_VOID__POINTER_POINTER,
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
signals [SIGNAL_RESUMING] =
g_signal_new ("resuming",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (UpBackendClass, resuming),
NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (klass, sizeof (UpBackendPrivate));
}
static DBusHandlerResult
message_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
UpBackend *backend = user_data;
if (dbus_message_is_signal (message, "org.freedesktop.UPower", "Resuming")) {
g_debug ("received Resuming signal");
g_signal_emit (backend, signals[SIGNAL_RESUMING], 0);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/**
* up_backend_init:
**/
@ -624,6 +679,15 @@ up_backend_init (UpBackend *backend)
backend->priv->daemon = NULL;
backend->priv->device_list = NULL;
backend->priv->managed_devices = up_device_list_new ();
#ifdef HAVE_SYSTEMD
if (sd_booted ()) {
DBusGConnection *bus;
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
backend->priv->connection = dbus_g_connection_get_connection (bus);
dbus_connection_add_filter (backend->priv->connection, message_filter, backend, NULL);
}
#endif
}
/**
@ -648,6 +712,9 @@ up_backend_finalize (GObject *object)
g_object_unref (backend->priv->managed_devices);
if (backend->priv->connection)
dbus_connection_remove_filter (backend->priv->connection, message_filter, backend);
G_OBJECT_CLASS (up_backend_parent_class)->finalize (object);
}

6
src/notify-upower.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/bash
[ "$1" = "post" ] && exec /usr/bin/dbus-send \
--system --type=signal \
--dest=org.freedesktop.UPower \
/org/freedesktop/UPower \
org.freedesktop.UPower.Resuming

View file

@ -205,6 +205,12 @@ up_backend_get_hibernate_command (UpBackend *backend)
return NULL;
}
gboolean
up_backend_emits_resuming (UpBackend *backend)
{
return FALSE;
}
/**
* up_backend_kernel_can_suspend:
**/

View file

@ -59,6 +59,7 @@ typedef struct
void (* device_removed) (UpBackend *backend,
GObject *native,
UpDevice *device);
void (* resuming) (UpBackend *backend);
} UpBackendClass;
GType up_backend_get_type (void);
@ -75,6 +76,7 @@ const gchar *up_backend_get_suspend_command (UpBackend *backend);
const gchar *up_backend_get_hibernate_command (UpBackend *backend);
const gchar *up_backend_get_powersave_command (UpBackend *backend,
gboolean powersave);
gboolean up_backend_emits_resuming (UpBackend *backend);
G_END_DECLS

View file

@ -380,8 +380,40 @@ typedef struct {
UpDaemon *daemon;
DBusGMethodInvocation *context;
gchar *command;
gulong handler;
} UpDaemonDeferredSleep;
static void
emit_resuming (UpDaemonDeferredSleep *sleep)
{
UpDaemon *daemon = sleep->daemon;
UpDaemonPrivate *priv = daemon->priv;
/* 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);
/* clear timer */
priv->about_to_sleep_id = 0;
priv->sent_sleeping_signal = FALSE;
/* delete temp object */
if (sleep->handler)
g_signal_handler_disconnect (priv->backend, sleep->handler);
g_object_unref (sleep->daemon);
g_free (sleep->command);
g_free (sleep);
}
/**
* up_daemon_deferred_sleep_cb:
**/
@ -396,7 +428,13 @@ up_daemon_deferred_sleep_cb (UpDaemonDeferredSleep *sleep)
UpDaemon *daemon = sleep->daemon;
UpDaemonPrivate *priv = daemon->priv;
if (up_backend_emits_resuming (priv->backend)) {
sleep->handler = g_signal_connect_swapped (priv->backend, "resuming",
G_CALLBACK (emit_resuming), sleep);
}
/* run the command */
g_debug ("Running %s", sleep->command);
ret = g_spawn_command_line_sync (sleep->command, &stdout, &stderr, NULL, &error_local);
if (!ret) {
error = g_error_new (UP_DAEMON_ERROR,
@ -408,32 +446,13 @@ up_daemon_deferred_sleep_cb (UpDaemonDeferredSleep *sleep)
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);
if (!up_backend_emits_resuming (priv->backend))
emit_resuming (sleep);
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;
}