mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
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>
This commit is contained in:
parent
fc17f194de
commit
c58493c00a
@ -175,7 +175,8 @@
|
||||
<literal>kexec</literal>,
|
||||
<literal>suspend</literal>,
|
||||
<literal>hibernate</literal>,
|
||||
<literal>hybrid-sleep</literal>, and
|
||||
<literal>hybrid-sleep</literal>,
|
||||
<literal>suspend-to-hibernate</literal>, and
|
||||
<literal>lock</literal>.
|
||||
Defaults to <literal>ignore</literal>.</para>
|
||||
|
||||
@ -224,7 +225,8 @@
|
||||
<literal>kexec</literal>,
|
||||
<literal>suspend</literal>,
|
||||
<literal>hibernate</literal>,
|
||||
<literal>hybrid-sleep</literal>, and
|
||||
<literal>hybrid-sleep</literal>,
|
||||
<literal>suspend-to-hibernate</literal>, and
|
||||
<literal>lock</literal>.
|
||||
If <literal>ignore</literal>, logind will never handle these
|
||||
keys. If <literal>lock</literal>, all running sessions will be
|
||||
|
@ -626,6 +626,7 @@ manpages = [
|
||||
'8',
|
||||
['systemd-hibernate.service',
|
||||
'systemd-hybrid-sleep.service',
|
||||
'systemd-suspend-to-hibernate.service',
|
||||
'systemd-sleep'],
|
||||
''],
|
||||
['systemd-sysctl.service', '8', ['systemd-sysctl'], ''],
|
||||
|
@ -60,7 +60,7 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd</command> supports three general
|
||||
<para><command>systemd</command> supports four general
|
||||
power-saving modes:</para>
|
||||
|
||||
<variablelist>
|
||||
@ -102,6 +102,17 @@
|
||||
suspend-to-both by the kernel.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>suspend-to-hibernate</term>
|
||||
|
||||
<listitem><para>A low power state where the system is initially suspended
|
||||
(the state is stored in RAM). If not interrupted within the delay specified by
|
||||
<command>HibernateDelaySec=</command>, the system will be woken using an RTC
|
||||
alarm and hibernated (the state is then stored on disk).
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>Settings in these files determine what strings
|
||||
@ -134,8 +145,9 @@
|
||||
<filename>/sys/power/disk</filename> by,
|
||||
respectively,
|
||||
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
|
||||
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
|
||||
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
More than one value can be specified by separating
|
||||
multiple values with whitespace. They will be tried
|
||||
in turn, until one is written without error. If
|
||||
@ -152,14 +164,24 @@
|
||||
<filename>/sys/power/state</filename> by,
|
||||
respectively,
|
||||
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
|
||||
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
|
||||
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
More than one value can be specified by separating
|
||||
multiple values with whitespace. They will be tried
|
||||
in turn, until one is written without error. If
|
||||
neither succeeds, the operation will be aborted.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>HibernateDelaySec=</varname></term>
|
||||
|
||||
<listitem><para>The amount of time in seconds
|
||||
that will pass before the system is automatically
|
||||
put into hibernate when using
|
||||
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
@ -180,6 +202,7 @@ SuspendState=freeze</programlisting></para>
|
||||
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
|
@ -50,6 +50,7 @@
|
||||
<refname>systemd-suspend.service</refname>
|
||||
<refname>systemd-hibernate.service</refname>
|
||||
<refname>systemd-hybrid-sleep.service</refname>
|
||||
<refname>systemd-suspend-to-hibernate.service</refname>
|
||||
<refname>systemd-sleep</refname>
|
||||
<refpurpose>System sleep state logic</refpurpose>
|
||||
</refnamediv>
|
||||
@ -58,6 +59,7 @@
|
||||
<para><filename>systemd-suspend.service</filename></para>
|
||||
<para><filename>systemd-hibernate.service</filename></para>
|
||||
<para><filename>systemd-hybrid-sleep.service</filename></para>
|
||||
<para><filename>systemd-suspend-to-hibernate.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/system-sleep</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -72,7 +74,9 @@
|
||||
hibernation. Finally,
|
||||
<filename>systemd-hybrid-sleep.service</filename> is pulled in by
|
||||
<filename>hybrid-sleep.target</filename> to execute hybrid
|
||||
hibernation with system suspend.</para>
|
||||
hibernation with system suspend and pulled in by
|
||||
<filename>suspend-to-hibernate.target</filename> to execute system suspend
|
||||
with a timeout that will activate hibernate later.</para>
|
||||
|
||||
<para>Immediately before entering system suspend and/or
|
||||
hibernation <filename>systemd-suspend.service</filename> (and the
|
||||
@ -80,8 +84,9 @@
|
||||
<filename>/usr/lib/systemd/system-sleep/</filename> and pass two
|
||||
arguments to them. The first argument will be
|
||||
<literal>pre</literal>, the second either
|
||||
<literal>suspend</literal>, <literal>hibernate</literal>, or
|
||||
<literal>hybrid-sleep</literal> depending on the chosen action.
|
||||
<literal>suspend</literal>, <literal>hibernate</literal>,
|
||||
<literal>hybrid-sleep</literal>, or <literal>suspend-to-hibernate</literal>
|
||||
depending on the chosen action.
|
||||
Immediately after leaving system suspend and/or hibernation the
|
||||
same executables are run, but the first argument is now
|
||||
<literal>post</literal>. All executables in this directory are
|
||||
@ -100,6 +105,7 @@
|
||||
<filename>systemd-suspend.service</filename>,
|
||||
<filename>systemd-hibernate.service</filename>, and
|
||||
<filename>systemd-hybrid-sleep.service</filename>
|
||||
<filename>systemd-suspend-to-hibernate.service</filename>
|
||||
should never be executed directly. Instead, trigger system sleep
|
||||
states with a command such as <literal>systemctl suspend</literal>
|
||||
or similar.</para>
|
||||
@ -128,9 +134,10 @@
|
||||
<term><option>suspend</option></term>
|
||||
<term><option>hibernate</option></term>
|
||||
<term><option>hybrid-sleep</option></term>
|
||||
<term><option>suspend-to-hibernate</option></term>
|
||||
|
||||
<listitem><para>Suspend, hibernate, or put the system to
|
||||
hybrid sleep.</para>
|
||||
<listitem><para>Suspend, hibernate, suspend to hibernate, or put the
|
||||
system to hybrid sleep.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
@ -65,6 +65,7 @@
|
||||
<filename>halt.target</filename>,
|
||||
<filename>hibernate.target</filename>,
|
||||
<filename>hybrid-sleep.target</filename>,
|
||||
<filename>suspend-to-hibernate.target</filename>,
|
||||
<filename>initrd-fs.target</filename>,
|
||||
<filename>initrd-root-device.target</filename>,
|
||||
<filename>initrd-root-fs.target</filename>,
|
||||
@ -307,6 +308,15 @@
|
||||
<filename>sleep.target</filename>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>suspend-to-hibernate.target</filename></term>
|
||||
<listitem>
|
||||
<para>A special target unit for suspending the system for a period
|
||||
of time, waking it and putting it into hibernate. This pulls in
|
||||
<filename>sleep.target</filename>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>halt.target</filename></term>
|
||||
<listitem>
|
||||
|
@ -205,8 +205,9 @@ _systemctl () {
|
||||
[JOBS]='cancel'
|
||||
[ENVS]='set-environment unset-environment import-environment'
|
||||
[STANDALONE]='daemon-reexec daemon-reload default
|
||||
emergency exit halt hibernate hybrid-sleep kexec list-jobs
|
||||
list-sockets list-timers list-units list-unit-files poweroff
|
||||
emergency exit halt hibernate hybrid-sleep
|
||||
suspend-to-hibernate kexec list-jobs list-sockets
|
||||
list-timers list-units list-unit-files poweroff
|
||||
reboot rescue show-environment suspend get-default
|
||||
is-system-running preset-all'
|
||||
[FILE]='link switch-root'
|
||||
|
@ -18,6 +18,7 @@
|
||||
"force-reload:Reload one or more units if possible, otherwise restart if active"
|
||||
"hibernate:Hibernate the system"
|
||||
"hybrid-sleep:Hibernate and suspend the system"
|
||||
"suspend-to-hibernate:Suspend the system for a period of time, and then hibernate it"
|
||||
"try-reload-or-restart:Reload one or more units if possible, otherwise restart if active"
|
||||
"isolate:Start one unit and stop all others"
|
||||
"kill:Send signal to processes of a unit"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -150,6 +150,10 @@
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="HybridSleep"/>
|
||||
|
||||
<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"/>
|
||||
@ -174,6 +178,10 @@
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="CanHybridSleep"/>
|
||||
|
||||
<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"/>
|
||||
|
@ -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"));
|
||||
|
||||
r = parse_sleep_config(verb, &modes, &states);
|
||||
if (streq(verb, "suspend-to-hibernate"))
|
||||
return can_s2h();
|
||||
|
||||
r = parse_sleep_config(verb, &modes, &states, NULL);
|
||||
if (r < 0)
|
||||
return false;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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[]) {
|
||||
|
@ -36,6 +36,7 @@ units = [
|
||||
['halt.target', ''],
|
||||
['hibernate.target', 'ENABLE_HIBERNATE'],
|
||||
['hybrid-sleep.target', 'ENABLE_HIBERNATE'],
|
||||
['suspend-to-hibernate.target', 'ENABLE_HIBERNATE'],
|
||||
['initrd-fs.target', ''],
|
||||
['initrd-root-device.target', ''],
|
||||
['initrd-root-fs.target', ''],
|
||||
@ -155,6 +156,7 @@ in_units = [
|
||||
['systemd-hibernate-resume@.service', 'ENABLE_HIBERNATE'],
|
||||
['systemd-hibernate.service', 'ENABLE_HIBERNATE'],
|
||||
['systemd-hybrid-sleep.service', 'ENABLE_HIBERNATE'],
|
||||
['systemd-suspend-to-hibernate.service', 'ENABLE_HIBERNATE'],
|
||||
['systemd-hostnamed.service', 'ENABLE_HOSTNAMED',
|
||||
'dbus-org.freedesktop.hostname1.service'],
|
||||
['systemd-hwdb-update.service', 'ENABLE_HWDB',
|
||||
|
16
units/suspend-to-hibernate.target
Normal file
16
units/suspend-to-hibernate.target
Normal file
@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Suspend; Idle into hibernate
|
||||
Documentation=man:systemd.special(7)
|
||||
DefaultDependencies=no
|
||||
Requires=systemd-suspend-to-hibernate.service
|
||||
After=systemd-suspend-to-hibernate.service
|
||||
StopWhenUnneeded=yes
|
19
units/systemd-suspend-to-hibernate.service.in
Normal file
19
units/systemd-suspend-to-hibernate.service.in
Normal file
@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Suspend; Idle into hibernate
|
||||
Documentation=man:systemd-suspend.service(8)
|
||||
DefaultDependencies=no
|
||||
Requires=sleep.target
|
||||
After=sleep.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@rootlibexecdir@/systemd-sleep suspend-to-hibernate
|
Loading…
Reference in New Issue
Block a user