shared/sleep-config: add switches to kill specific sleep modes

/etc/systemd/sleep.conf gains four new switches:
AllowSuspend=, AllowHibernation=, AllowSuspendThenHibernate=, AllowHybridSleep=.

Disabling specific modes was already possible by masking suspend.target,
hibernate.target, suspend-then-hibernate.target, or hybrid-sleep.target.
But this is not convenient for distributions, which want to set some defaults
based on what they want to support. Having those available as configuration
makes it easy to put a config file in /usr/lib/systemd/sleep.conf.d/ that
overrides the defaults and gives instructions how to undo that override.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-09-26 11:17:36 +02:00
parent 5fdf2d51c2
commit e8f1d00d69
6 changed files with 86 additions and 34 deletions

5
TODO
View File

@ -87,9 +87,8 @@ Features:
1. add resume_offset support to the resume code (i.e. support swap files 1. add resume_offset support to the resume code (i.e. support swap files
properly) properly)
2. check if swap is on weird storage and refuse if so 2. check if swap is on weird storage and refuse if so
3. add env-var based option to disable hibernation 3. figure out what to do with swap-on-luks
4. figure out what to do with swap-on-luks 4. add autodetection of hibernation images
5. add autodetection of hibernation images
* portables: introduce a new unit file directory /etc/systemd/system.attached/ * portables: introduce a new unit file directory /etc/systemd/system.attached/
or so, where we attach portable services to or so, where we attach portable services to

View File

@ -113,6 +113,24 @@
<filename>sleep.conf.d</filename> file:</para> <filename>sleep.conf.d</filename> file:</para>
<variablelist class='systemd-directives'> <variablelist class='systemd-directives'>
<varlistentry>
<term><varname>AllowSuspend=</varname></term>
<term><varname>AllowHibernation=</varname></term>
<term><varname>AllowSuspendThenHibernate=</varname></term>
<term><varname>AllowHybridSleep=</varname></term>
<listitem><para>By default any power-saving mode is advertised if possible (i.e.
the kernel supports that mode, the necessary resources are available). Those
switches can be used to disable specific modes.</para>
<para>If <varname>AllowHibernation=no</varname> or <varname>AllowSuspend=no</varname> is
used, this implies <varname>AllowSuspendThenHibernate=no</varname> and
<varname>AllowHybridSleep=no</varname>, since those methods use both suspend and hibernation
internally. <varname>AllowSuspendThenHibernate=yes</varname> and
<varname>AllowHybridSleep=yes</varname> can be used to override and enable those specific
modes.</para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>SuspendMode=</varname></term> <term><varname>SuspendMode=</varname></term>
<term><varname>HibernateMode=</varname></term> <term><varname>HibernateMode=</varname></term>

View File

@ -31,8 +31,10 @@
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t *_delay) { int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay) {
int allow_suspend = -1, allow_hibernate = -1,
allow_s2h = -1, allow_hybrid_sleep = -1;
bool allow;
_cleanup_strv_free_ char _cleanup_strv_free_ char
**suspend_mode = NULL, **suspend_state = NULL, **suspend_mode = NULL, **suspend_state = NULL,
**hibernate_mode = NULL, **hibernate_state = NULL, **hibernate_mode = NULL, **hibernate_state = NULL,
@ -41,13 +43,19 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
usec_t delay = 180 * USEC_PER_MINUTE; usec_t delay = 180 * USEC_PER_MINUTE;
const ConfigTableItem items[] = { const ConfigTableItem items[] = {
{ "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
{ "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state }, { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
{ "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode }, { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
{ "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state }, { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
{ "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
{ "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state }, { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
{ "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay}, { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
{ "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
{ "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},
{} {}
}; };
@ -57,6 +65,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
CONFIG_PARSE_WARN, NULL); CONFIG_PARSE_WARN, NULL);
if (streq(verb, "suspend")) { if (streq(verb, "suspend")) {
allow = allow_suspend != 0;
/* empty by default */ /* empty by default */
modes = TAKE_PTR(suspend_mode); modes = TAKE_PTR(suspend_mode);
@ -66,6 +76,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
states = strv_new("mem", "standby", "freeze", NULL); states = strv_new("mem", "standby", "freeze", NULL);
} else if (streq(verb, "hibernate")) { } else if (streq(verb, "hibernate")) {
allow = allow_hibernate != 0;
if (hibernate_mode) if (hibernate_mode)
modes = TAKE_PTR(hibernate_mode); modes = TAKE_PTR(hibernate_mode);
else else
@ -77,6 +89,9 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
states = strv_new("disk", NULL); states = strv_new("disk", NULL);
} else if (streq(verb, "hybrid-sleep")) { } else if (streq(verb, "hybrid-sleep")) {
allow = allow_hybrid_sleep > 0 ||
(allow_suspend != 0 && allow_hibernate != 0);
if (hybrid_mode) if (hybrid_mode)
modes = TAKE_PTR(hybrid_mode); modes = TAKE_PTR(hybrid_mode);
else else
@ -87,21 +102,26 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
else else
states = strv_new("disk", NULL); states = strv_new("disk", NULL);
} else if (streq(verb, "suspend-then-hibernate")) } else if (streq(verb, "suspend-then-hibernate")) {
allow = allow_s2h > 0 ||
(allow_suspend != 0 && allow_hibernate != 0);
modes = states = NULL; modes = states = NULL;
else } else
assert_not_reached("what verb"); assert_not_reached("what verb");
if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) || if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) ||
(!states && !streq(verb, "suspend-then-hibernate"))) (!states && !streq(verb, "suspend-then-hibernate")))
return log_oom(); return log_oom();
if (_modes) if (ret_allow)
*_modes = TAKE_PTR(modes); *ret_allow = allow;
if (_states) if (ret_modes)
*_states = TAKE_PTR(states); *ret_modes = TAKE_PTR(modes);
if (_delay) if (ret_states)
*_delay = delay; *ret_states = TAKE_PTR(states);
if (ret_delay)
*ret_delay = delay;
return 0; return 0;
} }
@ -506,6 +526,8 @@ int read_fiemap(int fd, struct fiemap **ret) {
return 0; return 0;
} }
static int can_sleep_internal(const char *verb, bool check_allowed);
static bool can_s2h(void) { static bool can_s2h(void) {
const char *p; const char *p;
int r; int r;
@ -518,7 +540,7 @@ static bool can_s2h(void) {
} }
FOREACH_STRING(p, "suspend", "hibernate") { FOREACH_STRING(p, "suspend", "hibernate") {
r = can_sleep(p); r = can_sleep_internal(p, false);
if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) { if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) {
log_debug("Unable to %s system.", p); log_debug("Unable to %s system.", p);
return false; return false;
@ -530,19 +552,25 @@ static bool can_s2h(void) {
return true; return true;
} }
int can_sleep(const char *verb) { static int can_sleep_internal(const char *verb, bool check_allowed) {
bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL; _cleanup_strv_free_ char **modes = NULL, **states = NULL;
int r; int r;
assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")); assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
if (streq(verb, "suspend-then-hibernate")) r = parse_sleep_config(verb, &allow, &modes, &states, NULL);
return can_s2h();
r = parse_sleep_config(verb, &modes, &states, NULL);
if (r < 0) if (r < 0)
return false; return false;
if (check_allowed && !allow) {
log_debug("Sleep mode \"%s\" is disabled by configuration.", verb);
return false;
}
if (streq(verb, "suspend-then-hibernate"))
return can_s2h();
if (!can_sleep_state(states) || !can_sleep_disk(modes)) if (!can_sleep_state(states) || !can_sleep_disk(modes))
return false; return false;
@ -564,3 +592,7 @@ int can_sleep(const char *verb) {
return true; return true;
} }
int can_sleep(const char *verb) {
return can_sleep_internal(verb, true);
}

View File

@ -5,7 +5,7 @@
#include "time-util.h" #include "time-util.h"
int read_fiemap(int fd, struct fiemap **ret); int read_fiemap(int fd, struct fiemap **ret);
int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay); int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay);
int find_hibernate_location(char **device, char **type, size_t *size, size_t *used); int find_hibernate_location(char **device, char **type, size_t *size, size_t *used);
int can_sleep(const char *verb); int can_sleep(const char *verb);

View File

@ -136,7 +136,6 @@ static int write_state(FILE **f, char **states) {
} }
static int execute(char **modes, char **states) { static int execute(char **modes, char **states) {
char *arguments[] = { char *arguments[] = {
NULL, NULL,
(char*) "pre", (char*) "pre",
@ -221,13 +220,11 @@ static int execute_s2h(usec_t hibernate_delay_sec) {
char time_str[DECIMAL_STR_MAX(uint64_t)]; char time_str[DECIMAL_STR_MAX(uint64_t)];
int r; int r;
r = parse_sleep_config("suspend", &suspend_modes, &suspend_states, r = parse_sleep_config("suspend", NULL, &suspend_modes, &suspend_states, NULL);
NULL);
if (r < 0) if (r < 0)
return r; return r;
r = parse_sleep_config("hibernate", &hibernate_modes, r = parse_sleep_config("hibernate", NULL, &hibernate_modes, &hibernate_states, NULL);
&hibernate_states, NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -340,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL; _cleanup_strv_free_ char **modes = NULL, **states = NULL;
usec_t delay = 0; usec_t delay = 0;
int r; int r;
@ -352,10 +350,15 @@ int main(int argc, char *argv[]) {
if (r <= 0) if (r <= 0)
goto finish; goto finish;
r = parse_sleep_config(arg_verb, &modes, &states, &delay); r = parse_sleep_config(arg_verb, &allow, &modes, &states, &delay);
if (r < 0) if (r < 0)
goto finish; goto finish;
if (!allow) {
log_error("Sleep mode \"%s\" is disabled by configuration, refusing.", arg_verb);
return EXIT_FAILURE;
}
if (streq(arg_verb, "suspend-then-hibernate")) if (streq(arg_verb, "suspend-then-hibernate"))
r = execute_s2h(delay); r = execute_s2h(delay);
else else

View File

@ -17,7 +17,7 @@ static void test_parse_sleep_config(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
FOREACH_STRING(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate") FOREACH_STRING(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")
assert_se(parse_sleep_config(verb, NULL, NULL, NULL) == 0); assert_se(parse_sleep_config(verb, NULL, NULL, NULL, NULL) == 0);
} }
static int test_fiemap(const char *path) { static int test_fiemap(const char *path) {