summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMario Limonciello <superm1@gmail.com>2018-03-08 21:17:33 +0800
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2018-03-08 14:17:33 +0100
commitc58493c00af97146d3b6c24da9c0371978124703 (patch)
treeeef8dce2e68194b33df3faec0631bee8543a175b /src
parentfc17f194ded93b51899aa0bbd4e66870d975fe5a (diff)
downloadsystemd-c58493c00af97146d3b6c24da9c0371978124703.tar.gz
Introduce suspend-to-hibernate (#8274)
Suspend to Hibernate is a new sleep method that invokes suspend for a predefined period of time before automatically waking up and hibernating the system. It's similar to HybridSleep however there isn't a performance impact on every suspend cycle. It's intended to use with systems that may have a higher power drain in their supported suspend states to prevent battery and data loss over an extended suspend cycle. Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Diffstat (limited to 'src')
-rw-r--r--src/basic/special.h1
-rw-r--r--src/login/logind-action.c13
-rw-r--r--src/login/logind-action.h1
-rw-r--r--src/login/logind-dbus.c29
-rw-r--r--src/login/org.freedesktop.login1.conf8
-rw-r--r--src/shared/sleep-config.c54
-rw-r--r--src/shared/sleep-config.h4
-rw-r--r--src/sleep/sleep.c94
-rw-r--r--src/systemctl/systemctl.c46
-rw-r--r--src/test/test-sleep.c1
10 files changed, 219 insertions, 32 deletions
diff --git a/src/basic/special.h b/src/basic/special.h
index c058b1d852..81078ffee9 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -37,6 +37,7 @@
#define SPECIAL_SUSPEND_TARGET "suspend.target"
#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
+#define SPECIAL_SUSPEND_TO_HIBERNATE_TARGET "suspend-to-hibernate.target"
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index 852ea9f949..0e8e0b2e4a 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -47,7 +47,8 @@ int manager_handle_action(
[HANDLE_KEXEC] = "Rebooting via kexec...",
[HANDLE_SUSPEND] = "Suspending...",
[HANDLE_HIBERNATE] = "Hibernating...",
- [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
+ [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
+ [HANDLE_SUSPEND_TO_HIBERNATE] = "Suspending to hibernate...",
};
static const char * const target_table[_HANDLE_ACTION_MAX] = {
@@ -57,7 +58,8 @@ int manager_handle_action(
[HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
[HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
[HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
- [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
+ [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
+ [HANDLE_SUSPEND_TO_HIBERNATE] = SPECIAL_SUSPEND_TO_HIBERNATE_TARGET,
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -110,6 +112,8 @@ int manager_handle_action(
supported = can_sleep("hibernate") > 0;
else if (handle == HANDLE_HYBRID_SLEEP)
supported = can_sleep("hybrid-sleep") > 0;
+ else if (handle == HANDLE_SUSPEND_TO_HIBERNATE)
+ supported = can_sleep("suspend-to-hibernate") > 0;
else if (handle == HANDLE_KEXEC)
supported = access(KEXEC, X_OK) >= 0;
else
@@ -125,7 +129,9 @@ int manager_handle_action(
return -EALREADY;
}
- inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
+ inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
+ HANDLE_HYBRID_SLEEP,
+ HANDLE_SUSPEND_TO_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
@@ -172,6 +178,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
[HANDLE_SUSPEND] = "suspend",
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
+ [HANDLE_SUSPEND_TO_HIBERNATE] = "suspend-to-hibernate",
[HANDLE_LOCK] = "lock"
};
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index 8c31ec42be..1ee8c812ff 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -29,6 +29,7 @@ typedef enum HandleAction {
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
+ HANDLE_SUSPEND_TO_HIBERNATE,
HANDLE_LOCK,
_HANDLE_ACTION_MAX,
_HANDLE_ACTION_INVALID = -1
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 07cb257151..ef5c478fef 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1933,6 +1933,20 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
error);
}
+static int method_suspend_to_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+
+ return method_do_shutdown_or_sleep(
+ m, message,
+ SPECIAL_SUSPEND_TO_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "hybrid-sleep",
+ error);
+}
+
static int nologin_timeout_handler(
sd_event_source *s,
uint64_t usec,
@@ -2386,6 +2400,19 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
error);
}
+static int method_can_suspend_to_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+
+ return method_can_shutdown_or_sleep(
+ m, message,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "suspend-to-hibernate",
+ error);
+}
+
static int property_get_reboot_to_firmware_setup(
sd_bus *bus,
const char *path,
@@ -2706,12 +2733,14 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SuspendToHibernate", "b", NULL, method_suspend_to_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CanSuspendToHibernate", NULL, "s", method_can_suspend_to_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index d842411781..970a217df1 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -152,6 +152,10 @@
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
+ send_member="SuspendToHibernate"/>
+
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Manager"
send_member="CanPowerOff"/>
<allow send_destination="org.freedesktop.login1"
@@ -176,6 +180,10 @@
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
+ send_member="CanSuspendToHibernate"/>
+
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Manager"
send_member="ScheduleShutdown"/>
<allow send_destination="org.freedesktop.login1"
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index ecac98e0ab..feb2c5281d 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -3,6 +3,7 @@
This file is part of systemd.
Copyright 2013 Zbigniew Jędrzejewski-Szmek
+ Copyright 2018 Dell Inc.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -41,13 +42,14 @@
#define USE(x, y) do { (x) = (y); (y) = NULL; } while (0)
-int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
+int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t *_delay) {
_cleanup_strv_free_ char
**suspend_mode = NULL, **suspend_state = NULL,
**hibernate_mode = NULL, **hibernate_state = NULL,
**hybrid_mode = NULL, **hybrid_state = NULL;
char **modes, **states;
+ usec_t delay;
const ConfigTableItem items[] = {
{ "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
@@ -56,6 +58,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
{ "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
{ "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
{ "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
+ { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay},
{}
};
@@ -94,18 +97,26 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
USE(states, hybrid_state);
else
states = strv_new("disk", NULL);
-
+ } else if (streq(verb, "suspend-to-hibernate")) {
+ if (delay == 0)
+ delay = 180 * USEC_PER_MINUTE;
} else
assert_not_reached("what verb");
- if ((!modes && !streq(verb, "suspend")) || !states) {
+ if ((!modes && (streq(verb, "hibernate") || streq(verb, "hybrid-sleep"))) ||
+ (!states && !streq(verb, "suspend-to-hibernate"))) {
strv_free(modes);
strv_free(states);
return log_oom();
}
- *_modes = modes;
- *_states = states;
+ if (_modes)
+ *_modes = modes;
+ if (_states)
+ *_states = states;
+ if (_delay)
+ *_delay = delay;
+
return 0;
}
@@ -260,15 +271,44 @@ static bool enough_memory_for_hibernation(void) {
return r;
}
+static bool can_s2h(void) {
+ int r;
+
+ r = access("/sys/class/rtc/rtc0/wakealarm", W_OK);
+ if (r < 0) {
+ log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+ "/sys/class/rct/rct0/wakealarm is not writable %m");
+ return false;
+ }
+
+ r = can_sleep("suspend");
+ if (r < 0) {
+ log_debug_errno(r, "Unable to suspend system.");
+ return false;
+ }
+
+ r = can_sleep("hibernate");
+ if (r < 0) {
+ log_debug_errno(r, "Unable to hibernate system.");
+ return false;
+ }
+
+ return true;
+}
+
int can_sleep(const char *verb) {
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
int r;
assert(streq(verb, "suspend") ||
streq(verb, "hibernate") ||
- streq(verb, "hybrid-sleep"));
+ streq(verb, "hybrid-sleep") ||
+ streq(verb, "suspend-to-hibernate"));
+
+ if (streq(verb, "suspend-to-hibernate"))
+ return can_s2h();
- r = parse_sleep_config(verb, &modes, &states);
+ r = parse_sleep_config(verb, &modes, &states, NULL);
if (r < 0)
return false;
diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h
index fc5a81d954..3dacda0d80 100644
--- a/src/shared/sleep-config.h
+++ b/src/shared/sleep-config.h
@@ -20,7 +20,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int parse_sleep_config(const char *verb, char ***modes, char ***states);
+#include "time-util.h"
+
+int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay);
int can_sleep(const char *verb);
int can_sleep_disk(char **types);
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 518032ec69..48e7c386f3 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -4,6 +4,7 @@
Copyright 2012 Lennart Poettering
Copyright 2013 Zbigniew Jędrzejewski-Szmek
+ Copyright 2018 Dell Inc.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -25,12 +26,14 @@
#include "sd-messages.h"
+#include "parse-util.h"
#include "def.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "sleep-config.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -135,6 +138,83 @@ static int execute(char **modes, char **states) {
return r;
}
+static int read_wakealarm(uint64_t *result) {
+ _cleanup_free_ char *t = NULL;
+
+ if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0)
+ return safe_atou64(t, result);
+ return -EBADF;
+}
+
+static int write_wakealarm(const char *str) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ f = fopen("/sys/class/rtc/rtc0/wakealarm", "we");
+ if (!f)
+ return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
+
+ r = write_string_stream(f, str, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str);
+
+ return 0;
+}
+
+static int execute_s2h(usec_t hibernate_delay_sec) {
+
+ _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL,
+ **suspend_modes = NULL, **suspend_states = NULL;
+ usec_t orig_time, cmp_time;
+ char time_str[DECIMAL_STR_MAX(uint64_t)];
+ int r;
+
+ r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = parse_sleep_config("hibernate", &hibernate_modes,
+ &hibernate_states, NULL);
+ if (r < 0)
+ return r;
+
+ r = read_wakealarm(&orig_time);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to read time: %d", r);
+
+ orig_time += hibernate_delay_sec / USEC_PER_SEC;
+ xsprintf(time_str, "%" PRIu64, orig_time);
+
+ r = write_wakealarm(time_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Set RTC wake alarm for %s", time_str);
+
+ r = execute(suspend_modes, suspend_states);
+ if (r < 0)
+ return r;
+
+ r = read_wakealarm(&cmp_time);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to read time: %d", r);
+
+ /* reset RTC */
+ r = write_wakealarm("0");
+ if (r < 0)
+ return r;
+
+ log_debug("Woke up at %"PRIu64, cmp_time);
+
+ /* if woken up after alarm time, hibernate */
+ if (cmp_time >= orig_time)
+ r = execute(hibernate_modes, hibernate_states);
+
+ return r;
+}
+
static void help(void) {
printf("%s COMMAND\n\n"
"Suspend the system, hibernate the system, or both.\n\n"
@@ -144,6 +224,8 @@ static void help(void) {
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
" hybrid-sleep Both hibernate and suspend the system\n"
+ " suspend-to-hibernate Initially suspend and then hibernate\n"
+ " the system after a fixed period of time\n"
, program_invocation_short_name);
}
@@ -189,7 +271,8 @@ static int parse_argv(int argc, char *argv[]) {
if (!streq(arg_verb, "suspend") &&
!streq(arg_verb, "hibernate") &&
- !streq(arg_verb, "hybrid-sleep")) {
+ !streq(arg_verb, "hybrid-sleep") &&
+ !streq(arg_verb, "suspend-to-hibernate")) {
log_error("Unknown command '%s'.", arg_verb);
return -EINVAL;
}
@@ -199,6 +282,7 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
+ usec_t delay = 0;
int r;
log_set_target(LOG_TARGET_AUTO);
@@ -209,12 +293,14 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = parse_sleep_config(arg_verb, &modes, &states);
+ r = parse_sleep_config(arg_verb, &modes, &states, &delay);
if (r < 0)
goto finish;
- r = execute(modes, states);
-
+ if (streq(arg_verb, "suspend-to-hibernate"))
+ r = execute_s2h(delay);
+ else
+ r = execute(modes, states);
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 1e975a5f2f..3b2538066d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -160,6 +160,7 @@ static enum action {
ACTION_SUSPEND,
ACTION_HIBERNATE,
ACTION_HYBRID_SLEEP,
+ ACTION_SUSPEND_TO_HIBERNATE,
ACTION_RUNLEVEL2,
ACTION_RUNLEVEL3,
ACTION_RUNLEVEL4,
@@ -3033,21 +3034,22 @@ static const struct {
const char *verb;
const char *mode;
} action_table[_ACTION_MAX] = {
- [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
- [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
- [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
- [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
- [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
- [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
- [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
- [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" },
- [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
- [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
- [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
- [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" },
- [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" },
- [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
- [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
+ [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
+ [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
+ [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
+ [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
+ [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" },
+ [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
+ [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
+ [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
+ [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" },
+ [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" },
+ [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
+ [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
+ [ACTION_SUSPEND_TO_HIBERNATE] = { SPECIAL_SUSPEND_TO_HIBERNATE_TARGET, "suspend-to-hibernate", "replace-irreversibly" },
};
static enum action verb_to_action(const char *verb) {
@@ -3278,6 +3280,11 @@ static int logind_reboot(enum action a) {
description = "put system into hybrid sleep";
break;
+ case ACTION_SUSPEND_TO_HIBERNATE:
+ method = "SuspendToHibernate";
+ description = "put system into suspend followed by hibernate";
+ break;
+
default:
return -EINVAL;
}
@@ -3629,7 +3636,8 @@ static int start_special(int argc, char *argv[], void *userdata) {
ACTION_HALT,
ACTION_SUSPEND,
ACTION_HIBERNATE,
- ACTION_HYBRID_SLEEP)) {
+ ACTION_HYBRID_SLEEP,
+ ACTION_SUSPEND_TO_HIBERNATE)) {
r = logind_reboot(a);
if (r >= 0)
@@ -7326,7 +7334,9 @@ static void systemctl_help(void) {
" switch-root ROOT [INIT] Change to a different root file system\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
- " hybrid-sleep Hibernate and suspend the system\n",
+ " hybrid-sleep Hibernate and suspend the system\n"
+ " suspend-to-hibernate Suspend the system, wake after a period of\n"
+ " time and put it into hibernate\n",
program_invocation_short_name);
}
@@ -8423,6 +8433,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
{ "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "suspend-to-hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
{ "default", VERB_ANY, 1, VERB_ONLINE_ONLY, start_special },
{ "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
{ "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
@@ -8754,6 +8765,7 @@ int main(int argc, char*argv[]) {
case ACTION_SUSPEND:
case ACTION_HIBERNATE:
case ACTION_HYBRID_SLEEP:
+ case ACTION_SUSPEND_TO_HIBERNATE:
case ACTION_EMERGENCY:
case ACTION_DEFAULT:
/* systemctl verbs with no equivalent in the legacy commands.
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index 3c2b115fbf..e49ecbe494 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -48,6 +48,7 @@ static void test_sleep(void) {
log_info("Suspend configured and possible: %s", yes_no(can_sleep("suspend") > 0));
log_info("Hibernation configured and possible: %s", yes_no(can_sleep("hibernate") > 0));
log_info("Hybrid-sleep configured and possible: %s", yes_no(can_sleep("hybrid-sleep") > 0));
+ log_info("Suspend-to-Hibernate configured and possible: %s", yes_no(can_sleep("suspend-to-hibernate") > 0));
}
int main(int argc, char* argv[]) {