Package: systemd-shim Version: 9-1 Severity: normal Tags: upstream patch Hi,
the systemd-shim is currently lacking support for hybrid sleep. Since e.g. upower by default tries to trigger hybrid sleep on laptops when the battery goes critically low, this can lead to data loss. (To make the current situation worse, invoking org.freedesktop.login1.Manager.HybridSleep on the dbus fails with the misleading error message Error org.freedesktop.DBus.Error.FileNotFound: Unknown unit: hybrid-sleep.target which does *not* imply that /lib/systemd/system/hybrid-sleep.target does not exist.) I have cooked up a patch that adds hybrid sleep support to the systemd shim. It would be great if you could apply it to the Debian package. Thanks, Nikolaus -- System Information: Debian Release: 8.0 APT prefers unstable APT policy: (500, 'unstable') Architecture: i386 (i686) Foreign Architectures: armhf Kernel: Linux 3.16.0-4-686-pae (SMP w/2 CPU cores) Locale: LANG=de_DE.utf8, LC_CTYPE=en_GB.utf8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: sysvinit (via /sbin/init) Versions of packages systemd-shim depends on: ii cgmanager 0.36-2 ii libc6 2.19-17 ii libglib2.0-0 2.42.1-1 systemd-shim recommends no packages. Versions of packages systemd-shim suggests: ii pm-utils 1.4.1-15 -- no debconf information
diff --git a/src/power-unit.c b/src/power-unit.c index 45dc70c..b55c1b7 100644 --- a/src/power-unit.c +++ b/src/power-unit.c @@ -32,7 +32,8 @@ const gchar *power_cmds[] = { [POWER_OFF] = "/sbin/shutdown -h now", [POWER_REBOOT] = "/sbin/reboot", [POWER_SUSPEND] = "/usr/sbin/pm-suspend", - [POWER_HIBERNATE] = "/usr/sbin/pm-hibernate" + [POWER_HIBERNATE] = "/usr/sbin/pm-hibernate", + [POWER_HYBRID_SLEEP] = "/usr/sbin/pm-suspend-hybrid" }; typedef struct @@ -45,6 +46,61 @@ G_DEFINE_TYPE (PowerUnit, power_unit, UNIT_TYPE) gboolean in_shutdown; +static gint +set_hibernation_mode (const gchar *mode, gchar **previous_mode) +{ + gint fd, cnt; + + g_return_val_if_fail (mode != NULL, -1); + + fd = open ("/sys/power/disk", O_RDWR); + if (fd == -1) + { + g_warning ("Could not open /sys/power/disk"); + return -1; + } + + if (previous_mode) + { + /* When reading /sys/power/disk, each supported mode is output with a + * space appended, and the active mode is put in brackets. + */ + gchar hibernation_modes[sizeof "platform [shutdown] reboot suspend \n"]; + gchar *active_hibernation_mode, *end; + + *previous_mode = NULL; + + cnt = read (fd, hibernation_modes, sizeof hibernation_modes); + if (cnt == -1 || cnt == sizeof hibernation_modes) + goto parse_error; + + active_hibernation_mode = strchr (hibernation_modes, '['); + if (active_hibernation_mode == NULL) + goto parse_error; + ++active_hibernation_mode; + end = strchr (active_hibernation_mode, ']'); + if (end == NULL) + goto parse_error; + *end = '\0'; + + if (!g_str_equal (active_hibernation_mode, "disabled")) + *previous_mode = g_strdup (active_hibernation_mode); + } + + cnt = write (fd, mode, strlen (mode)); + if (cnt != strlen (mode)) + goto error; + + close (fd); + return 0; + +parse_error: + g_warning ("Error decoding /sys/power/disk"); +error: + close (fd); + return -1; +} + static void power_unit_start (Unit *unit) { @@ -89,7 +145,8 @@ power_unit_start (Unit *unit) * timestamp of the event that caused the suspend all the way * down. */ - if (pu->action == POWER_SUSPEND && last_suspend_time + G_TIME_SPAN_SECOND > g_get_monotonic_time ()) + if ((pu->action == POWER_SUSPEND || pu->action == POWER_HYBRID_SLEEP) && + last_suspend_time + G_TIME_SPAN_SECOND > g_get_monotonic_time ()) return; /* pm-utils might not have been installed, so go the direct route @@ -104,6 +161,13 @@ power_unit_start (Unit *unit) { const gchar *kind; gint fd; + gchar *previous_hibernation_mode; + + if (pu->action == POWER_HYBRID_SLEEP) + { + if (set_hibernation_mode ("suspend", &previous_hibernation_mode) != 0) + g_warning ("Could not set hibernation mode"); + } fd = open ("/sys/power/state", O_WRONLY); if (fd == -1) @@ -116,9 +180,16 @@ power_unit_start (Unit *unit) if (write (fd, kind, strlen (kind)) != strlen (kind)) g_warning ("Failed to write() to /sys/power/state?!?"); close (fd); + + if (previous_hibernation_mode) + { + if (set_hibernation_mode (previous_hibernation_mode, NULL) != 0) + g_warning ("Could not reset hibernation mode"); + g_free (previous_hibernation_mode); + } } - if (pu->action == POWER_SUSPEND) + if (pu->action == POWER_SUSPEND || pu->action == POWER_HYBRID_SLEEP) last_suspend_time = g_get_monotonic_time (); } } diff --git a/src/unit.c b/src/unit.c index d04c94e..27d21ef 100644 --- a/src/unit.c +++ b/src/unit.c @@ -46,6 +46,9 @@ lookup_unit (const gchar *unit_name, else if (g_str_equal (unit_name, "hibernate.target")) unit = power_unit_new (POWER_HIBERNATE); + else if (g_str_equal (unit_name, "hybrid-sleep.target")) + unit = power_unit_new (POWER_HYBRID_SLEEP); + else if (g_str_equal (unit_name, "reboot.target")) unit = power_unit_new (POWER_REBOOT); diff --git a/src/unit.h b/src/unit.h index 8e83925..a93821b 100644 --- a/src/unit.h +++ b/src/unit.h @@ -54,6 +54,7 @@ typedef enum POWER_REBOOT, POWER_SUSPEND, POWER_HIBERNATE, + POWER_HYBRID_SLEEP, N_POWER_ACTIONS } PowerAction;