From 195b36cf21a0ef490df0f626bd3c817a599a528e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Jun 2018 11:38:43 +0200 Subject: [PATCH 01/13] bootspec: fix include lines List all files we use definitions from. --- src/shared/bootspec.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index f47c073a46..cd1b5e5011 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -2,7 +2,11 @@ #pragma once -#include +#include +#include +#include + +#include "sd-id128.h" typedef struct BootEntry { char *filename; From 12580bc3eda2105b608210c72d791f41d1b9f0d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Jun 2018 12:18:43 +0200 Subject: [PATCH 02/13] bootspec: rename "filename" field to "id" This follows the renaming done a few commits earlier too systemd-boot itself. Also, let's show the ID, since it's useful. --- src/boot/bootctl.c | 2 ++ src/shared/bootspec.c | 30 +++++++++++++++--------------- src/shared/bootspec.h | 5 ++--- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 8680b3a6ae..827927f18b 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1029,6 +1029,8 @@ static int verb_list(int argc, char *argv[], void *userdata) { ansi_highlight_green(), n == (unsigned) config.default_entry ? " (default)" : "", ansi_normal()); + if (e->id) + printf(" id: %s\n", e->id); if (e->version) printf(" version: %s\n", e->version); if (e->machine_id) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 8d5a421144..fc0a6728ab 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -23,7 +23,7 @@ void boot_entry_free(BootEntry *entry) { assert(entry); - free(entry->filename); + free(entry->id); free(entry->title); free(entry->show_title); free(entry->version); @@ -53,8 +53,8 @@ int boot_entry_load(const char *path, BootEntry *entry) { } b = basename(path); - tmp.filename = strndup(b, c - b); - if (!tmp.filename) + tmp.id = strndup(b, c - b); + if (!tmp.id) return log_oom(); f = fopen(path, "re"); @@ -203,7 +203,7 @@ int boot_loader_read_conf(const char *path, BootConfig *config) { } static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { - return str_verscmp(a->filename, b->filename); + return str_verscmp(a->id, b->id); } int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) { @@ -300,7 +300,7 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { /* Add file name to non-unique titles */ for (i = 0; i < n_entries; i++) if (arr[i]) { - r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].filename); + r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id); if (r < 0) return -ENOMEM; @@ -317,30 +317,30 @@ static int boot_entries_select_default(const BootConfig *config) { if (config->entry_oneshot) for (i = config->n_entries - 1; i >= 0; i--) - if (streq(config->entry_oneshot, config->entries[i].filename)) { - log_debug("Found default: filename \"%s\" is matched by LoaderEntryOneShot", - config->entries[i].filename); + if (streq(config->entry_oneshot, config->entries[i].id)) { + log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot", + config->entries[i].id); return i; } if (config->entry_default) for (i = config->n_entries - 1; i >= 0; i--) - if (streq(config->entry_default, config->entries[i].filename)) { - log_debug("Found default: filename \"%s\" is matched by LoaderEntryDefault", - config->entries[i].filename); + if (streq(config->entry_default, config->entries[i].id)) { + log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault", + config->entries[i].id); return i; } if (config->default_pattern) for (i = config->n_entries - 1; i >= 0; i--) - if (fnmatch(config->default_pattern, config->entries[i].filename, FNM_CASEFOLD) == 0) { - log_debug("Found default: filename \"%s\" is matched by pattern \"%s\"", - config->entries[i].filename, config->default_pattern); + if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) { + log_debug("Found default: id \"%s\" is matched by pattern \"%s\"", + config->entries[i].id, config->default_pattern); return i; } if (config->n_entries > 0) - log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].filename); + log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id); else log_debug("Found no default boot entry :("); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index cd1b5e5011..614df698e6 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -9,8 +9,7 @@ #include "sd-id128.h" typedef struct BootEntry { - char *filename; - + char *id; char *title; char *show_title; char *version; @@ -48,7 +47,7 @@ void boot_config_free(BootConfig *config); int boot_entries_load_config(const char *esp_path, BootConfig *config); static inline const char* boot_entry_title(const BootEntry *entry) { - return entry->show_title ?: entry->title ?: entry->filename; + return entry->show_title ?: entry->title ?: entry->id; } int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); From 0307ea49c70b1ed4f3af3c80566c8c098dc080bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 07:15:55 +0200 Subject: [PATCH 03/13] Add $SYSTEMD_IN_INITRD=yes|no override for debugging --- docs/ENVIRONMENT.md | 4 ++++ src/basic/util.c | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 9d598a6693..ec804c9c85 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -37,6 +37,10 @@ All tools: useful for debugging, in order to test generators and other code against specific kernel command lines. +* `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection. + This is useful for debugging and testing initrd-only programs in the main + system. + * `$SYSTEMD_BUS_TIMEOUT=SECS` — specifies the maximum time to wait for method call completion. If no time unit is specified, assumes seconds. The usual other units are understood, too (us, ms, s, min, h, d, w, month, y). If it is not set or set diff --git a/src/basic/util.c b/src/basic/util.c index 081c63c898..0da963f4af 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -23,6 +23,7 @@ #include "def.h" #include "device-nodes.h" #include "dirent-util.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -106,6 +107,7 @@ int prot_from_flags(int flags) { bool in_initrd(void) { struct statfs s; + int r; if (saved_in_initrd >= 0) return saved_in_initrd; @@ -120,9 +122,16 @@ bool in_initrd(void) { * emptying when transititioning to the main systemd. */ - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); + r = getenv_bool_secure("SYSTEMD_IN_INITRD"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); + + if (r >= 0) + saved_in_initrd = r > 0; + else + saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && + statfs("/", &s) >= 0 && + is_temporary_fs(&s); return saved_in_initrd; } From a79858bfd1937e089893fee4d0d729a0ae8cfc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 07:23:03 +0200 Subject: [PATCH 04/13] hib-res-generator: open logging before emitting the first message Also add a debug message when we are not in initrd, because it can be confusing why nothing is happenning. --- .../hibernate-resume-generator.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 4b79e3def4..8ff44c3bb9 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -60,6 +60,13 @@ static int process_resume(void) { int main(int argc, char *argv[]) { int r = 0; + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); return EXIT_FAILURE; @@ -68,16 +75,11 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_prohibit_ipc(true); - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - /* Don't even consider resuming outside of initramfs. */ - if (!in_initrd()) + if (!in_initrd()) { + log_debug("Not running in an initrd, quitting."); return EXIT_SUCCESS; + } r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) From e83419d043e4f1e11f52d9a3360b0e5bdea71031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 07:31:10 +0200 Subject: [PATCH 05/13] hib-res-generator: add "noresume" This is an override parameter, to totally skip dehiberanation. --- TODO | 5 ++--- man/systemd-hibernate-resume-generator.xml | 14 +++++++++++--- .../hibernate-resume-generator.c | 17 +++++++++++++++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index f848b9741e..59ea95746c 100644 --- a/TODO +++ b/TODO @@ -86,11 +86,10 @@ Features: 1. add resume_offset support to the resume code (i.e. support swap files properly) - 2. check of 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 4. figure out what to do with swap-on-luks - 5. add autodetection of hibernation images, and add "noresume" to disable - this + 5. 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-hibernate-resume-generator.xml b/man/systemd-hibernate-resume-generator.xml index 86d45dc4af..8f0cc5d044 100644 --- a/man/systemd-hibernate-resume-generator.xml +++ b/man/systemd-hibernate-resume-generator.xml @@ -28,11 +28,13 @@ Description - systemd-hibernate-resume-generator is a - generator that instantiates + systemd-hibernate-resume-generator is a + generator that initiates the procedure to resume the system from hibernation. + It instantiates the systemd-hibernate-resume@.service8 unit according to the value of parameter - specified on the kernel command line. + specified on the kernel command line, which will instruct the kernel + to resume the system from the hibernation image on that device. @@ -54,6 +56,12 @@ supported. + + noresume + + Do not try to resume from hibernation. If this parameter is + present, resume= is ignored. + diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 8ff44c3bb9..036493a389 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -15,6 +15,7 @@ static const char *arg_dest = "/tmp"; static char *arg_resume_device = NULL; +static bool arg_noresume = false; static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { @@ -28,8 +29,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!s) return log_oom(); - free(arg_resume_device); - arg_resume_device = s; + free_and_replace(arg_resume_device, s); + + } else if (streq(key, "noresume")) { + if (value) { + log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring."); + return 0; + } + + arg_noresume = true; } return 0; @@ -85,6 +93,11 @@ int main(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + if (arg_noresume) { + log_notice("Found \"noresume\" on the kernel command line, quitting."); + return EXIT_SUCCESS; + } + r = process_resume(); free(arg_resume_device); From 1b20d88987d0785da7e694fd0e002c14142e08e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 07:45:56 +0200 Subject: [PATCH 06/13] Move logic to find default sd-boot entry from systemctl to shared In preparation for use in other places. No functional change. --- src/shared/bootspec.c | 33 +++++++++++++++++++++++++++++++++ src/shared/bootspec.h | 2 ++ src/systemctl/systemctl.c | 17 ++++------------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index fc0a6728ab..7a6399eaa7 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -598,3 +598,36 @@ found: return 0; } + +int find_default_boot_entry( + const char *esp_path, + char **esp_where, + BootConfig *config, + const BootEntry **e) { + + _cleanup_free_ char *where = NULL; + int r; + + assert(config); + assert(e); + + r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL); + if (r < 0) + return r; + + r = boot_entries_load_config(where, config); + if (r < 0) + return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where); + + if (config->default_entry < 0) { + log_error("No entry suitable as default, refusing to guess."); + return -ENOENT; + } + + *e = &config->entries[config->default_entry]; + + if (esp_where) + *esp_where = TAKE_PTR(where); + + return 0; +} diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 614df698e6..e0fcaaea6f 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -51,3 +51,5 @@ static inline const char* boot_entry_title(const BootEntry *entry) { } int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); + +int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index ce5cbe7c13..be1b7375af 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3455,21 +3455,12 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_esp_and_warn(arg_esp_path, false, &where, NULL, NULL, NULL, NULL); - if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */ + r = find_default_boot_entry(arg_esp_path, &where, &config, &e); + if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ return log_error_errno(r, "Cannot find the ESP partition mount point."); - if (r < 0) /* But it logs about all these cases, hence don't log here again */ - return r; - - r = boot_entries_load_config(where, &config); if (r < 0) - return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where); - - if (config.default_entry < 0) { - log_error("No entry suitable as default, refusing to guess."); - return -ENOENT; - } - e = &config.entries[config.default_entry]; + /* But it logs about all these cases, hence don't log here again */ + return r; if (strv_length(e->initrd) > 1) { log_error("Boot entry specifies multiple initrds, which is not supported currently."); From 2d3bfb6904e2a855eebf1e8273fe2d21dc363077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 08:35:12 +0200 Subject: [PATCH 07/13] shared/bootspec: remember the full path to boot entry and use it in logging It's much easier to understand what is going on when the full path is logged. --- src/shared/bootspec.c | 6 ++++++ src/shared/bootspec.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 7a6399eaa7..5d8471c51a 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -24,6 +24,7 @@ void boot_entry_free(BootEntry *entry) { assert(entry); free(entry->id); + free(entry->path); free(entry->title); free(entry->show_title); free(entry->version); @@ -57,6 +58,10 @@ int boot_entry_load(const char *path, BootEntry *entry) { if (!tmp.id) return log_oom(); + tmp.path = strdup(path); + if (!tmp.path) + return log_oom(); + f = fopen(path, "re"); if (!f) return log_error_errno(errno, "Failed to open \"%s\": %m", path); @@ -625,6 +630,7 @@ int find_default_boot_entry( } *e = &config->entries[config->default_entry]; + log_debug("Found default boot entry in file \"%s\"", (*e)->path); if (esp_where) *esp_where = TAKE_PTR(where); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index e0fcaaea6f..c39d773cb3 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -9,7 +9,8 @@ #include "sd-id128.h" typedef struct BootEntry { - char *id; + char *id; /* This is the file basename without extension */ + char *path; /* This is the full path to the file */ char *title; char *show_title; char *version; From 1af294fc82059975ce5f8f566a21385245773a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 08:45:25 +0200 Subject: [PATCH 08/13] Drop empty lines in proc-cmdline.c --- src/basic/proc-cmdline.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index add481c2ae..c7c4018df1 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -40,7 +40,6 @@ int proc_cmdline(char **ret) { } int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) { - _cleanup_free_ char *line = NULL; const char *p; int r; @@ -87,14 +86,12 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla } static bool relaxed_equal_char(char a, char b) { - return a == b || (a == '_' && b == '-') || (a == '-' && b == '_'); } char *proc_cmdline_key_startswith(const char *s, const char *prefix) { - assert(s); assert(prefix); From 9a135c084ae6d06734ec3380f0eafdecd7c216f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 09:29:09 +0200 Subject: [PATCH 09/13] basic/proc-cmdline: allow parsing of cmdline from a string Comes with tests. Also add direct test for $SYSTEMD_PROC_CMDLINE. In test-proc-cmdline, "true" was masquerading as PROC_CMDLINE_STRIP_RD_PREFIX, fix that. Also, reorder functions to match call order. --- src/basic/proc-cmdline.c | 20 +++++--- src/basic/proc-cmdline.h | 1 + src/test/test-proc-cmdline.c | 94 ++++++++++++++++++++++++++++++------ 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index c7c4018df1..b386c705e1 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -39,17 +39,12 @@ int proc_cmdline(char **ret) { return read_one_line_file("/proc/cmdline", ret); } -int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) { - _cleanup_free_ char *line = NULL; +int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags) { const char *p; int r; assert(parse_item); - r = proc_cmdline(&line); - if (r < 0) - return r; - p = line; for (;;) { _cleanup_free_ char *word = NULL; @@ -85,6 +80,19 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla return 0; } +int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) { + _cleanup_free_ char *line = NULL; + int r; + + assert(parse_item); + + r = proc_cmdline(&line); + if (r < 0) + return r; + + return proc_cmdline_parse_given(line, parse_item, data, flags); +} + static bool relaxed_equal_char(char a, char b) { return a == b || (a == '_' && b == '-') || diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h index 4a9e6e0f62..ffc45fddb9 100644 --- a/src/basic/proc-cmdline.h +++ b/src/basic/proc-cmdline.h @@ -14,6 +14,7 @@ typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *da int proc_cmdline(char **ret); +int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags); int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags); int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value); diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 8f77e084b6..5db103bd22 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" +#include "env-util.h" #include "log.h" #include "macro.h" #include "proc-cmdline.h" @@ -19,28 +20,68 @@ static int parse_item(const char *key, const char *value, void *data) { } static void test_proc_cmdline_parse(void) { - assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0); + log_info("/* %s */", __func__); + + assert_se(proc_cmdline_parse(parse_item, &obj, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0); } -static void test_runlevel_to_target(void) { - in_initrd_force(false); - assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); - assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); - assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL)); - assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET)); - assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL)); +static void test_proc_cmdline_override(void) { + log_info("/* %s */", __func__); - in_initrd_force(true); - assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); - assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); - assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL)); - assert_se(streq_ptr(runlevel_to_target("3"), NULL)); - assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET)); + assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm") == 0); + + /* Test if the override works */ + _cleanup_free_ char *line = NULL, *value = NULL; + assert_se(proc_cmdline(&line) >= 0); + + /* Test if parsing makes uses of the override */ + assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm")); + assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux")); +} + +static int parse_item_given(const char *key, const char *value, void *data) { + assert_se(key); + assert_se(data); + + bool *strip = data; + + log_info("%s: option <%s> = <%s>", __func__, key, strna(value)); + if (streq(key, "foo_bar")) + assert_se(streq(value, "quux")); + else if (streq(key, "wuff-piep")) + assert_se(streq(value, "tuet ")); + else if (in_initrd() && *strip && streq(key, "zumm")) + assert_se(!value); + else if (in_initrd() && !*strip && streq(key, "rd.zumm")) + assert_se(!value); + else + assert_not_reached("Bad key!"); + + return 0; +} + +static void test_proc_cmdline_given(bool flip_initrd) { + log_info("/* %s (flip: %s) */", __func__, yes_no(flip_initrd)); + + if (flip_initrd) + in_initrd_force(!in_initrd()); + + bool t = true, f = false; + assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm", + parse_item_given, &t, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0); + + assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm", + parse_item_given, &f, 0) >= 0); + + + if (flip_initrd) + in_initrd_force(!in_initrd()); } static void test_proc_cmdline_get_key(void) { _cleanup_free_ char *value = NULL; + log_info("/* %s */", __func__); putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm"); assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL); @@ -78,6 +119,7 @@ static void test_proc_cmdline_get_key(void) { static void test_proc_cmdline_get_bool(void) { bool value = false; + log_info("/* %s */", __func__); putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep"); assert_se(proc_cmdline_get_bool("", &value) == -EINVAL); @@ -94,6 +136,7 @@ static void test_proc_cmdline_get_bool(void) { } static void test_proc_cmdline_key_streq(void) { + log_info("/* %s */", __func__); assert_se(proc_cmdline_key_streq("", "")); assert_se(proc_cmdline_key_streq("a", "a")); @@ -110,6 +153,7 @@ static void test_proc_cmdline_key_streq(void) { } static void test_proc_cmdline_key_startswith(void) { + log_info("/* %s */", __func__); assert_se(proc_cmdline_key_startswith("", "")); assert_se(proc_cmdline_key_startswith("x", "")); @@ -124,11 +168,33 @@ static void test_proc_cmdline_key_startswith(void) { assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx")); } +static void test_runlevel_to_target(void) { + log_info("/* %s */", __func__); + + in_initrd_force(false); + assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); + assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); + assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL)); + assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET)); + assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL)); + + in_initrd_force(true); + assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); + assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); + assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL)); + assert_se(streq_ptr(runlevel_to_target("3"), NULL)); + assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET)); +} + int main(void) { log_parse_environment(); log_open(); test_proc_cmdline_parse(); + test_proc_cmdline_override(); + test_proc_cmdline_given(false); + /* Repeat the same thing, but now flip our ininitrdness */ + test_proc_cmdline_given(true); test_proc_cmdline_key_streq(); test_proc_cmdline_key_startswith(); test_proc_cmdline_get_key(); From edf43e3df7a72e42f3653b7516256c859c47b2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 10:15:16 +0200 Subject: [PATCH 10/13] test-sleep: print function names --- src/test/test-sleep.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 2ce79f8345..2d614a913c 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -14,6 +14,8 @@ static void test_parse_sleep_config(void) { const char *verb; + log_info("/* %s */", __func__); + FOREACH_STRING(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate") assert_se(parse_sleep_config(verb, NULL, NULL, NULL) == 0); } @@ -23,6 +25,8 @@ static int test_fiemap(const char *path) { _cleanup_close_ int fd = -1; int r; + log_info("/* %s */", __func__); + fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK); if (fd < 0) return log_error_errno(errno, "failed to open %s: %m", path); @@ -56,7 +60,9 @@ static void test_sleep(void) { **freez = strv_new("freeze", NULL); int r; - log_info("/* configuration */"); + log_info("/* %s */", __func__); + + log_info("/= configuration =/"); log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0)); log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0)); log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0)); @@ -66,7 +72,7 @@ static void test_sleep(void) { log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0)); log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0)); - log_info("/* running system */"); + log_info("/= running system =/"); r = can_sleep("suspend"); log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r)); r = can_sleep("hibernate"); From 5fdf2d51c244288ac41443d1bd81365fab7b7b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 10:15:46 +0200 Subject: [PATCH 11/13] shared/sleep-config: forbid hibernation if resume= is not configured --- src/login/logind-dbus.c | 4 +- src/shared/sleep-config.c | 95 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 5035bb5bdf..8e22538ac3 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1773,6 +1773,8 @@ static int method_do_shutdown_or_sleep( return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation"); if (r == -ENOMEDIUM) return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Kernel image has been removed, can't hibernate"); + if (r == -EADV) + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Resume not configured, can't hibernate"); if (r == 0) return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb \"%s\" not supported", sleep_verb); if (r < 0) @@ -2199,7 +2201,7 @@ static int method_can_shutdown_or_sleep( if (sleep_verb) { r = can_sleep(sleep_verb); - if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) + if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) return sd_bus_reply_method_return(message, "s", "na"); if (r < 0) return r; diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 2ffd32bf99..fba6851d28 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -16,6 +16,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "bootspec.h" #include "conf-parser.h" #include "def.h" #include "env-util.h" @@ -25,6 +26,7 @@ #include "macro.h" #include "parse-util.h" #include "path-util.h" +#include "proc-cmdline.h" #include "sleep-config.h" #include "string-util.h" #include "strv.h" @@ -266,12 +268,94 @@ static bool enough_swap_for_hibernation(void) { } r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; - log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", - r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); + log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", + r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); return r; } +static int check_resume_keys(const char *key, const char *value, void *data) { + assert_se(key); + assert_se(data); + + int *resume = data; + + if (*resume == 0) + /* Exit if we already know we can't resume. */ + return 0; + + if (streq(key, "noresume")) { + log_debug("Found \"noresume\" on the kernel command line, hibernation is disabled."); + *resume = 0; + + } else if (streq(key, "resume")) { + log_debug("Found resume= option on the kernel command line, hibernation is possible."); + *resume = 1; + } + + return 0; +} + +static int resume_configured_in_options(const char *options) { + int resume = -1, r; + + /* We don't use PROC_CMDLINE_STRIP_RD_PREFIX here, so rd.resume is *not* supported. */ + r = proc_cmdline_parse_given(options, check_resume_keys, &resume, 0); + if (r < 0) + return r; + + if (resume < 0) + log_debug("Couldn't find resume= option, hibernation is disabled."); + return resume > 0; +} + +static int resume_configured(void) { + _cleanup_(boot_config_free) BootConfig config = {}; + const BootEntry *e; + int r; + + /* Check whether a valid resume= option is present. If possible, we query the boot options + * for the default kernel. If the system is not using sd-boot, fall back to checking the + * current kernel command line. This is not perfect, but should suffice for most cases. */ + + r = find_default_boot_entry(NULL, NULL, &config, &e); + if (r == -ENOKEY) + log_debug_errno(r, "Cannot find the ESP partition mount point, falling back to other checks."); + else if (r < 0) + return log_debug_errno(r, "Cannot read boot configuration from ESP, assuming hibernation is not possible."); + else { + _cleanup_free_ char *options = NULL; + + options = strv_join(e->options, " "); + if (!options) + return log_oom(); + + r = resume_configured_in_options(options); + if (r < 0) + return log_error_errno(r, "Failed to parse kernel options in \"%s\": %m", + strnull(e->path)); + return r; + } + + /* If we can't figure out the default boot entry, let's fall back to current kernel cmdline */ + _cleanup_free_ char *line = NULL; + r = proc_cmdline(&line); + if (IN_SET(r, -EPERM, -EACCES, -ENOENT)) + log_debug_errno(r, "Cannot access /proc/cmdline: %m"); + else if (r < 0) + return log_error_errno(r, "Failed to query /proc/cmdline: %m"); + else { + r = resume_configured_in_options(line); + if (r < 0) + return log_error_errno(r, "Failed to parse kernel proc cmdline: %m"); + + return r; + } + + log_debug("Couldn't detect any resume mechanism, hibernation is disabled."); + return false; +} + static int kernel_exists(void) { struct utsname u; sd_id128_t m; @@ -435,7 +519,7 @@ static bool can_s2h(void) { FOREACH_STRING(p, "suspend", "hibernate") { r = can_sleep(p); - if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) { + if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) { log_debug("Unable to %s system.", p); return false; } @@ -473,5 +557,10 @@ int can_sleep(const char *verb) { if (!enough_swap_for_hibernation()) return -ENOSPC; + r = resume_configured(); + if (r <= 0) + /* We squash all errors (e.g. EPERM) into a single value for reporting. */ + return -EADV; + return true; } From e8f1d00d695f491654d50e57c82623288d3bcbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 11:17:36 +0200 Subject: [PATCH 12/13] 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. --- TODO | 5 +-- man/systemd-sleep.conf.xml | 18 +++++++++ src/shared/sleep-config.c | 78 +++++++++++++++++++++++++++----------- src/shared/sleep-config.h | 2 +- src/sleep/sleep.c | 15 +++++--- src/test/test-sleep.c | 2 +- 6 files changed, 86 insertions(+), 34 deletions(-) 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) { From 9259d0e23ed09d1f9ea6b04e916df191181eadcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Sep 2018 11:18:25 +0200 Subject: [PATCH 13/13] TODO: remove entry Swap on luks works great. Nothing to do here. --- TODO | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TODO b/TODO index e1f3dfb7cc..3a45ff14fe 100644 --- a/TODO +++ b/TODO @@ -87,8 +87,7 @@ 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. figure out what to do with swap-on-luks - 4. add autodetection of hibernation images + 3. add autodetection of hibernation images * portables: introduce a new unit file directory /etc/systemd/system.attached/ or so, where we attach portable services to