diff --git a/TODO b/TODO index 59ea95746c..e1f3dfb7cc 100644 --- a/TODO +++ b/TODO @@ -87,9 +87,8 @@ Features: 1. add resume_offset support to the resume code (i.e. support swap files properly) 2. check if swap is on weird storage and refuse if so - 3. add env-var based option to disable hibernation - 4. figure out what to do with swap-on-luks - 5. add autodetection of hibernation images + 3. figure out what to do with swap-on-luks + 4. add autodetection of hibernation images * portables: introduce a new unit file directory /etc/systemd/system.attached/ or so, where we attach portable services to diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml index 10bbc8c76a..96e6d5e452 100644 --- a/man/systemd-sleep.conf.xml +++ b/man/systemd-sleep.conf.xml @@ -113,6 +113,24 @@ sleep.conf.d file: + + AllowSuspend= + AllowHibernation= + AllowSuspendThenHibernate= + AllowHybridSleep= + + 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. + + If AllowHibernation=no or AllowSuspend=no is + used, this implies AllowSuspendThenHibernate=no and + AllowHybridSleep=no, since those methods use both suspend and hibernation + internally. AllowSuspendThenHibernate=yes and + AllowHybridSleep=yes can be used to override and enable those specific + modes. + + SuspendMode= HibernateMode= diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index fba6851d28..6778399188 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -31,8 +31,10 @@ #include "string-util.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 **suspend_mode = NULL, **suspend_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; const ConfigTableItem items[] = { - { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, - { "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}, + { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend }, + { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate }, + { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h }, + { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep }, + + { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, + { "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); if (streq(verb, "suspend")) { + allow = allow_suspend != 0; + /* empty by default */ 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); } else if (streq(verb, "hibernate")) { + allow = allow_hibernate != 0; + if (hibernate_mode) modes = TAKE_PTR(hibernate_mode); else @@ -77,6 +89,9 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t states = strv_new("disk", NULL); } else if (streq(verb, "hybrid-sleep")) { + allow = allow_hybrid_sleep > 0 || + (allow_suspend != 0 && allow_hibernate != 0); + if (hybrid_mode) modes = TAKE_PTR(hybrid_mode); else @@ -87,21 +102,26 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t else 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; - else + } else assert_not_reached("what verb"); if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) || (!states && !streq(verb, "suspend-then-hibernate"))) return log_oom(); - if (_modes) - *_modes = TAKE_PTR(modes); - if (_states) - *_states = TAKE_PTR(states); - if (_delay) - *_delay = delay; + if (ret_allow) + *ret_allow = allow; + if (ret_modes) + *ret_modes = TAKE_PTR(modes); + if (ret_states) + *ret_states = TAKE_PTR(states); + if (ret_delay) + *ret_delay = delay; return 0; } @@ -506,6 +526,8 @@ int read_fiemap(int fd, struct fiemap **ret) { return 0; } +static int can_sleep_internal(const char *verb, bool check_allowed); + static bool can_s2h(void) { const char *p; int r; @@ -518,7 +540,7 @@ static bool can_s2h(void) { } FOREACH_STRING(p, "suspend", "hibernate") { - r = can_sleep(p); + r = can_sleep_internal(p, false); if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) { log_debug("Unable to %s system.", p); return false; @@ -530,19 +552,25 @@ static bool can_s2h(void) { 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; int r; assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")); - if (streq(verb, "suspend-then-hibernate")) - return can_s2h(); - - r = parse_sleep_config(verb, &modes, &states, NULL); + r = parse_sleep_config(verb, &allow, &modes, &states, NULL); if (r < 0) 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)) return false; @@ -564,3 +592,7 @@ int can_sleep(const char *verb) { return true; } + +int can_sleep(const char *verb) { + return can_sleep_internal(verb, true); +} diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 6bf035969a..c584f44d39 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -5,7 +5,7 @@ #include "time-util.h" 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 can_sleep(const char *verb); diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 042a44656f..0085cb0196 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -136,7 +136,6 @@ static int write_state(FILE **f, char **states) { } static int execute(char **modes, char **states) { - char *arguments[] = { NULL, (char*) "pre", @@ -221,13 +220,11 @@ static int execute_s2h(usec_t hibernate_delay_sec) { char time_str[DECIMAL_STR_MAX(uint64_t)]; int r; - r = parse_sleep_config("suspend", &suspend_modes, &suspend_states, - NULL); + r = parse_sleep_config("suspend", NULL, &suspend_modes, &suspend_states, NULL); if (r < 0) return r; - r = parse_sleep_config("hibernate", &hibernate_modes, - &hibernate_states, NULL); + r = parse_sleep_config("hibernate", NULL, &hibernate_modes, &hibernate_states, NULL); if (r < 0) return r; @@ -340,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) { } int main(int argc, char *argv[]) { + bool allow; _cleanup_strv_free_ char **modes = NULL, **states = NULL; usec_t delay = 0; int r; @@ -352,10 +350,15 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - r = parse_sleep_config(arg_verb, &modes, &states, &delay); + r = parse_sleep_config(arg_verb, &allow, &modes, &states, &delay); if (r < 0) 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")) r = execute_s2h(delay); else diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 2d614a913c..442541a298 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -17,7 +17,7 @@ static void test_parse_sleep_config(void) { log_info("/* %s */", __func__); 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) {