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
- 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
+ 2. check if swap is on weird storage and refuse if so
+ 3. add autodetection of hibernation images
* cgroups: use inotify to get notified when somebody else modifies cgroups
owned by us, then log a friendly warning.
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
<refsect1>
<title>Description</title>
- <para><filename>systemd-hibernate-resume-generator</filename> is a
- generator that instantiates
+ <para><command>systemd-hibernate-resume-generator</command> is a
+ generator that initiates the procedure to resume the system from hibernation.
+ It instantiates the
<citerefentry><refentrytitle>systemd-hibernate-resume@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
unit according to the value of <option>resume=</option> parameter
- specified on the kernel command line.</para>
+ specified on the kernel command line, which will instruct the kernel
+ to resume the system from the hibernation image on that device.</para>
</refsect1>
<refsect1>
supported.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>noresume</varname></term>
+
+ <listitem><para>Do not try to resume from hibernation. If this parameter is
+ present, <varname>resume=</varname> is ignored.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<filename>sleep.conf.d</filename> file:</para>
<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>
<term><varname>SuspendMode=</varname></term>
<term><varname>HibernateMode=</varname></term>
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;
return 0;
}
-static bool relaxed_equal_char(char a, char b) {
+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 == '-') ||
(a == '-' && b == '_');
}
char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
-
assert(s);
assert(prefix);
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);
#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"
bool in_initrd(void) {
struct statfs s;
+ int r;
if (saved_in_initrd >= 0)
return saved_in_initrd;
* 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;
}
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)
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) {
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;
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;
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)
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);
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)
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;
void boot_entry_free(BootEntry *entry) {
assert(entry);
- free(entry->filename);
+ free(entry->id);
+ free(entry->path);
free(entry->title);
free(entry->show_title);
free(entry->version);
}
b = basename(path);
- tmp.filename = strndup(b, c - b);
- if (!tmp.filename)
+ tmp.id = strndup(b, c - b);
+ if (!tmp.id)
+ return log_oom();
+
+ tmp.path = strdup(path);
+ if (!tmp.path)
return log_oom();
f = fopen(path, "re");
}
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) {
/* 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;
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 :(");
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];
+ log_debug("Found default boot entry in file \"%s\"", (*e)->path);
+
+ if (esp_where)
+ *esp_where = TAKE_PTR(where);
+
+ return 0;
+}
#pragma once
-#include <stdlib.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
-typedef struct BootEntry {
- char *filename;
+#include "sd-id128.h"
+typedef struct BootEntry {
+ 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;
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);
+
+int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e);
#include "sd-id128.h"
#include "alloc-util.h"
+#include "bootspec.h"
#include "conf-parser.h"
#include "def.h"
#include "env-util.h"
#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"
-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,
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},
{}
};
CONFIG_PARSE_WARN, NULL);
if (streq(verb, "suspend")) {
+ allow = allow_suspend != 0;
+
/* empty by default */
modes = TAKE_PTR(suspend_mode);
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
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
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;
}
}
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;
return 0;
}
+static int can_sleep_internal(const char *verb, bool check_allowed);
+
static bool can_s2h(void) {
const char *p;
int r;
}
FOREACH_STRING(p, "suspend", "hibernate") {
- r = can_sleep(p);
- if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) {
+ r = can_sleep_internal(p, false);
+ if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) {
log_debug("Unable to %s system.", p);
return false;
}
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;
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;
}
+
+int can_sleep(const char *verb) {
+ return can_sleep_internal(verb, true);
+}
#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);
}
static int execute(char **modes, char **states) {
-
char *arguments[] = {
NULL,
(char*) "pre",
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;
}
int main(int argc, char *argv[]) {
+ bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
usec_t delay = 0;
int r;
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
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.");
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
+#include "env-util.h"
#include "log.h"
#include "macro.h"
#include "proc-cmdline.h"
}
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);
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);
}
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"));
}
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", ""));
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();
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);
+ assert_se(parse_sleep_config(verb, NULL, NULL, NULL, NULL) == 0);
}
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);
**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));
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");