summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2023-09-05 15:50:04 +0200
committerMike Yuan <me@yhndnzj.com>2023-09-07 14:21:16 +0200
commita628d933cc67cc8b183dc809ba1451aa5b2996e5 (patch)
tree4440309c1b40d99372a6dffcded8f680e4f541f7
parentMerge pull request #28957 from yuwata/core-mount-set-dirty-on-umount (diff)
downloadsystemd-a628d933cc67cc8b183dc809ba1451aa5b2996e5.tar.xz
systemd-a628d933cc67cc8b183dc809ba1451aa5b2996e5.zip
hibernate-resume: split out the logic of finding hibernate location
Before this commit, the hibernate location logic only exists in the generator. Also, we compare device nodes (devnode_same()) and clear EFI variable HibernateLocation in the generator too. This is not ideal though: when the generator gets to run, udev hasn't yet started, so effectively devnode_same() always fails. Moreover, if the boot process is interrupted by e.g. battery-check, the hibernate information is lost. Therefore, let's split out the logic of finding hibernate location. The generator only does the initial validation of system info and enables systemd-hibernate-resume.service, and when the service actually runs we validate everything again, which includes comparing the device nodes and clearing the EFI variable. This should make things more robust, plus systems that don't utilize a systemd-enabled initrd can use the exact same logic to resume using the EFI variable. I.e., systemd-hibernate-resume can be used standalone.
-rw-r--r--src/hibernate-resume/hibernate-resume-config.c264
-rw-r--r--src/hibernate-resume/hibernate-resume-config.h55
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c236
-rw-r--r--src/hibernate-resume/hibernate-resume.c56
-rw-r--r--src/hibernate-resume/meson.build4
-rw-r--r--units/meson.build4
-rw-r--r--units/systemd-hibernate-resume.service.in24
7 files changed, 423 insertions, 220 deletions
diff --git a/src/hibernate-resume/hibernate-resume-config.c b/src/hibernate-resume/hibernate-resume-config.c
new file mode 100644
index 0000000000..4aac6234d0
--- /dev/null
+++ b/src/hibernate-resume/hibernate-resume-config.c
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/utsname.h>
+
+#include "alloc-util.h"
+#include "device-nodes.h"
+#include "fstab-util.h"
+#include "hibernate-resume-config.h"
+#include "json.h"
+#include "os-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
+#include "efivars.h"
+
+static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLocation *k) {
+ if (!k)
+ return NULL;
+
+ free(k->device);
+
+ return mfree(k);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
+
+static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
+ if (!e)
+ return NULL;
+
+ free(e->device);
+
+ free(e->kernel_version);
+ free(e->id);
+ free(e->image_id);
+ free(e->image_version);
+
+ return mfree(e);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
+
+void hibernate_info_done(HibernateInfo *info) {
+ assert(info);
+
+ kernel_hibernate_location_free(info->cmdline);
+ efi_hibernate_location_free(info->efi);
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ KernelHibernateLocation *k = ASSERT_PTR(data);
+ int r;
+
+ assert(key);
+
+ if (streq(key, "resume")) {
+ _cleanup_free_ char *d = NULL;
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ d = fstab_node_to_udev_node(value);
+ if (!d)
+ return log_oom();
+
+ free_and_replace(k->device, d);
+
+ } else if (proc_cmdline_key_streq(key, "resume_offset")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = safe_atou64(value, &k->offset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resume_offset=%s: %m", value);
+
+ k->offset_set = true;
+ }
+
+ return 0;
+}
+
+static int get_kernel_hibernate_location(KernelHibernateLocation **ret) {
+ _cleanup_(kernel_hibernate_location_freep) KernelHibernateLocation *k = NULL;
+ int r;
+
+ assert(ret);
+
+ k = new0(KernelHibernateLocation, 1);
+ if (!k)
+ return log_oom();
+
+ r = proc_cmdline_parse(parse_proc_cmdline_item, k, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse kernel command line: %m");
+
+ if (!k->device) {
+ if (k->offset_set)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Found resume_offset=%" PRIu64 " but resume= is unset, refusing.",
+ k->offset);
+
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = TAKE_PTR(k);
+ return 1;
+}
+
+#if ENABLE_EFI
+static bool validate_efi_hibernate_location(EFIHibernateLocation *e) {
+ _cleanup_free_ char *id = NULL, *image_id = NULL, *version_id = NULL, *image_version = NULL;
+ struct utsname uts = {};
+ int r;
+
+ assert(e);
+
+ if (uname(&uts) < 0)
+ log_warning_errno(errno, "Failed to get kernel info, ignoring: %m");
+
+ r = parse_os_release(NULL,
+ "ID", &id,
+ "IMAGE_ID", &image_id,
+ "VERSION_ID", &version_id,
+ "IMAGE_VERSION", &image_version);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse os-release, ignoring: %m");
+
+ if (!streq(uts.release, strempty(e->kernel_version)) ||
+ !streq_ptr(id, e->id) ||
+ !streq_ptr(image_id, e->image_id) ||
+ !streq_ptr(version_id, e->version_id) ||
+ !streq_ptr(image_version, e->image_version)) {
+
+ log_notice("HibernateLocation system info doesn't match with current running system, not resuming from it.");
+ return false;
+ }
+
+ return true;
+}
+
+static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), JSON_MANDATORY },
+ { "offset", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), JSON_MANDATORY },
+ { "kernelVersion", JSON_VARIANT_STRING, json_dispatch_string, offsetof(EFIHibernateLocation, kernel_version), JSON_PERMISSIVE|JSON_DEBUG },
+ { "osReleaseId", JSON_VARIANT_STRING, json_dispatch_string, offsetof(EFIHibernateLocation, id), JSON_PERMISSIVE|JSON_DEBUG },
+ { "osReleaseImageId", JSON_VARIANT_STRING, json_dispatch_string, offsetof(EFIHibernateLocation, image_id), JSON_PERMISSIVE|JSON_DEBUG },
+ { "osReleaseVersionId", JSON_VARIANT_STRING, json_dispatch_string, offsetof(EFIHibernateLocation, version_id), JSON_PERMISSIVE|JSON_DEBUG },
+ { "osReleaseImageVersion", JSON_VARIANT_STRING, json_dispatch_string, offsetof(EFIHibernateLocation, image_version), JSON_PERMISSIVE|JSON_DEBUG },
+ {},
+ };
+
+ _cleanup_(efi_hibernate_location_freep) EFIHibernateLocation *e = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ char *location_str = NULL;
+ int r;
+
+ assert(ret);
+
+ if (!is_efi_boot())
+ goto skip;
+
+ r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(HibernateLocation), &location_str);
+ if (r == -ENOENT) {
+ log_debug_errno(r, "EFI variable HibernateLocation is not set, skipping.");
+ goto skip;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get EFI variable HibernateLocation: %m");
+
+ r = json_parse(location_str, 0, &v, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse HibernateLocation JSON object: %m");
+
+ e = new0(EFIHibernateLocation, 1);
+ if (!e)
+ return log_oom();
+
+ r = json_dispatch(v, dispatch_table, NULL, JSON_LOG, e);
+ if (r < 0)
+ return r;
+
+ if (!validate_efi_hibernate_location(e))
+ goto skip;
+
+ if (asprintf(&e->device, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(e->uuid)) < 0)
+ return log_oom();
+
+ *ret = TAKE_PTR(e);
+ return 1;
+
+skip:
+ *ret = NULL;
+ return 0;
+}
+
+void compare_hibernate_location_and_warn(const HibernateInfo *info) {
+ int r;
+
+ assert(info);
+ assert(info->from_efi || info->cmdline);
+
+ if (info->from_efi)
+ return;
+ if (!info->efi)
+ return;
+
+ if (!path_equal(info->cmdline->device, info->efi->device)) {
+ r = devnode_same(info->cmdline->device, info->efi->device);
+ if (r < 0)
+ log_warning_errno(r,
+ "Failed to check if resume=%s is the same device as EFI HibernateLocation device '%s', ignoring: %m",
+ info->cmdline->device, info->efi->device);
+ if (r == 0)
+ log_warning("resume=%s doesn't match with EFI HibernateLocation device '%s', proceeding anyway with resume=.",
+ info->cmdline->device, info->efi->device);
+ }
+
+ if (info->cmdline->offset != info->efi->offset)
+ log_warning("resume_offset=%" PRIu64 " doesn't match with EFI HibernateLocation offset %" PRIu64 ", proceeding anyway with resume_offset=.",
+ info->cmdline->offset, info->efi->offset);
+}
+
+void clear_efi_hibernate_location(void) {
+ int r;
+
+ if (!is_efi_boot())
+ return;
+
+ r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
+}
+#endif
+
+int acquire_hibernate_info(HibernateInfo *ret) {
+ _cleanup_(hibernate_info_done) HibernateInfo i = {};
+ int r;
+
+ r = get_kernel_hibernate_location(&i.cmdline);
+ if (r < 0)
+ return r;
+
+#if ENABLE_EFI
+ r = get_efi_hibernate_location(&i.efi);
+ if (r < 0)
+ return r;
+#endif
+
+ if (i.cmdline) {
+ i.device = i.cmdline->device;
+ i.from_efi = false;
+ } else if (i.efi) {
+ i.device = i.efi->device;
+ i.from_efi = true;
+ } else
+ return -ENODEV;
+
+ *ret = TAKE_STRUCT(i);
+ return 0;
+}
diff --git a/src/hibernate-resume/hibernate-resume-config.h b/src/hibernate-resume/hibernate-resume-config.h
new file mode 100644
index 0000000000..a77d29c891
--- /dev/null
+++ b/src/hibernate-resume/hibernate-resume-config.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdbool.h>
+
+#include "sd-id128.h"
+
+typedef struct KernelHibernateLocation {
+ char *device;
+ uint64_t offset;
+ bool offset_set;
+} KernelHibernateLocation;
+
+typedef struct EFIHibernateLocation {
+ char *device;
+
+ sd_id128_t uuid;
+ uint64_t offset;
+
+ char *kernel_version;
+ char *id;
+ char *image_id;
+ char *version_id;
+ char *image_version;
+} EFIHibernateLocation;
+
+typedef struct HibernateInfo {
+ const char *device;
+ uint64_t offset; /* in memory pages */
+ bool from_efi;
+
+ KernelHibernateLocation *cmdline;
+ EFIHibernateLocation *efi;
+} HibernateInfo;
+
+void hibernate_info_done(HibernateInfo *info);
+
+int acquire_hibernate_info(HibernateInfo *ret);
+
+#if ENABLE_EFI
+
+void compare_hibernate_location_and_warn(const HibernateInfo *info);
+
+void clear_efi_hibernate_location(void);
+
+#else
+
+static inline void compare_hibernate_location_and_warn(const HibernateInfo *info) {
+ return;
+}
+
+static inline void clear_efi_hibernate_location(void) {
+ return;
+}
+
+#endif
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 8b83ad6bdb..c1c21a2c13 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -1,87 +1,31 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
-#include <stdio.h>
-#include <sys/utsname.h>
-#include <unistd.h>
-
-#include "sd-id128.h"
-
#include "alloc-util.h"
-#include "device-nodes.h"
#include "dropin.h"
-#include "efivars.h"
-#include "escape.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "fstab-util.h"
#include "generator.h"
-#include "id128-util.h"
+#include "hibernate-resume-config.h"
#include "initrd-util.h"
-#include "json.h"
#include "log.h"
#include "main-func.h"
-#include "os-util.h"
#include "parse-util.h"
-#include "path-util.h"
#include "proc-cmdline.h"
#include "special.h"
+#include "static-destruct.h"
#include "string-util.h"
#include "unit-name.h"
static const char *arg_dest = NULL;
-static char *arg_resume_device = NULL;
static char *arg_resume_options = NULL;
static char *arg_root_options = NULL;
static bool arg_noresume = false;
-static uint64_t arg_resume_offset = 0;
-static bool arg_resume_offset_set = false;
-STATIC_DESTRUCTOR_REGISTER(arg_resume_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
-#if ENABLE_EFI
-typedef struct EFIHibernateLocation {
- sd_id128_t uuid;
- uint64_t offset;
- const char *kernel_version;
- const char *id;
- const char *image_id;
- const char *version_id;
- const char *image_version;
-} EFIHibernateLocation;
-#endif
-
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
- int r;
-
assert(key);
- if (streq(key, "resume")) {
- char *s;
-
- if (proc_cmdline_value_missing(key, value))
- return 0;
-
- s = fstab_node_to_udev_node(value);
- if (!s)
- return log_oom();
-
- free_and_replace(arg_resume_device, s);
-
- } else if (proc_cmdline_key_streq(key, "resume_offset")) {
-
- if (proc_cmdline_value_missing(key, value))
- return 0;
-
- r = safe_atou64(value, &arg_resume_offset);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resume_offset=%s: %m", value);
-
- arg_resume_offset_set = true;
-
- } else if (streq(key, "resumeflags")) {
+ if (streq(key, "resumeflags")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -100,7 +44,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
} else if (streq(key, "noresume")) {
if (value) {
- log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
+ log_warning("'noresume' kernel command line option specified with an argument, ignoring.");
return 0;
}
@@ -110,106 +54,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
-static int parse_efi_hibernate_location(void) {
- int r = 0;
-
-#if ENABLE_EFI
- static const JsonDispatch dispatch_table[] = {
- { "uuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), JSON_MANDATORY },
- { "offset", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), JSON_MANDATORY },
- { "kernelVersion", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, kernel_version), JSON_PERMISSIVE|JSON_DEBUG },
- { "osReleaseId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, id), JSON_PERMISSIVE|JSON_DEBUG },
- { "osReleaseImageId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, image_id), JSON_PERMISSIVE|JSON_DEBUG },
- { "osReleaseVersionId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, version_id), JSON_PERMISSIVE|JSON_DEBUG },
- { "osReleaseImageVersion", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, image_version), JSON_PERMISSIVE|JSON_DEBUG },
- {},
- };
-
- _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- _cleanup_free_ char *location_str = NULL, *device = NULL, *id = NULL, *image_id = NULL,
- *version_id = NULL, *image_version = NULL;
- struct utsname uts = {};
- EFIHibernateLocation location = {};
-
- r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(HibernateLocation), &location_str);
- if (r == -ENOENT) {
- log_debug_errno(r, "EFI variable HibernateLocation is not set, skipping.");
- return 0;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to get EFI variable HibernateLocation: %m");
-
- r = json_parse(location_str, 0, &v, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to parse HibernateLocation JSON object: %m");
-
- r = json_dispatch(v, dispatch_table, NULL, JSON_LOG, &location);
- if (r < 0)
- return r;
-
- if (uname(&uts) < 0)
- log_warning_errno(errno, "Failed to get kernel info, ignoring: %m");
-
- r = parse_os_release(NULL,
- "ID", &id,
- "IMAGE_ID", &image_id,
- "VERSION_ID", &version_id,
- "IMAGE_VERSION", &image_version);
- if (r < 0)
- log_warning_errno(r, "Failed to parse os-release, ignoring: %m");
-
- if (!streq(uts.release, strempty(location.kernel_version)) ||
- !streq_ptr(id, location.id) ||
- !streq_ptr(image_id, location.image_id) ||
- !streq_ptr(version_id, location.version_id) ||
- !streq_ptr(image_version, location.image_version)) {
-
- log_notice("HibernateLocation system info doesn't match with current running system, not resuming from it.");
- return 0;
- }
-
- if (asprintf(&device, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(location.uuid)) < 0)
- return log_oom();
-
- if (!arg_resume_device) {
- arg_resume_device = TAKE_PTR(device);
- arg_resume_offset = location.offset;
- } else {
- if (!path_equal(arg_resume_device, device)) {
- r = devnode_same(arg_resume_device, device);
- if (r < 0)
- log_debug_errno(r,
- "Failed to check if resume=%s is the same device as HibernateLocation device '%s', ignoring: %m",
- arg_resume_device, device);
- if (r == 0)
- log_warning("resume=%s doesn't match with HibernateLocation device '%s', proceeding anyway with resume=.",
- arg_resume_device, device);
- }
-
- if (arg_resume_offset != location.offset)
- log_warning("resume_offset=%" PRIu64 " doesn't match with HibernateLocation offset %" PRIu64 ", proceeding anyway with resume_offset=.",
- arg_resume_offset, location.offset);
- }
-
- r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
- if (r < 0)
- log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
-#endif
-
- return r;
-}
-
-static int process_resume(void) {
- _cleanup_free_ char *device_unit = NULL, *device_escaped = NULL;
- _cleanup_fclose_ FILE *f = NULL;
+static int process_resume(const char *device) {
+ _cleanup_free_ char *device_unit = NULL;
int r;
- if (!arg_resume_device)
- return 0;
+ assert(device);
- r = unit_name_from_path(arg_resume_device, ".device", &device_unit);
+ r = unit_name_from_path(device, ".device", &device_unit);
if (r < 0)
- return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", arg_resume_device);
+ return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", device);
r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
"# Automatically generated by systemd-hibernate-resume-generator\n\n"
@@ -218,59 +71,35 @@ static int process_resume(void) {
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
- device_escaped = cescape(arg_resume_device);
- if (!device_escaped)
- return log_oom();
-
- r = generator_open_unit_file(arg_dest, NULL, SPECIAL_HIBERNATE_RESUME_SERVICE, &f);
- if (r < 0)
- return r;
-
- fprintf(f,
- "[Unit]\n"
- "Description=Resume from hibernation\n"
- "Documentation=man:systemd-hibernate-resume.service(8)\n"
- "DefaultDependencies=no\n"
- "BindsTo=%1$s\n"
- "Wants=local-fs-pre.target\n"
- "After=%1$s\n"
- "Before=local-fs-pre.target\n"
- "AssertPathExists=/etc/initrd-release\n"
- "\n"
- "[Service]\n"
- "Type=oneshot\n"
- "ExecStart=" LIBEXECDIR "/systemd-hibernate-resume %2$s %3$" PRIu64 "\n",
- device_unit,
- device_escaped,
- arg_resume_offset);
-
- r = fflush_and_check(f);
- if (r < 0)
- return log_error_errno(r, "Failed to create " SPECIAL_HIBERNATE_RESUME_SERVICE ": %m");
-
- r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE);
- if (r < 0)
- return r;
-
r = generator_write_timeouts(arg_dest,
- arg_resume_device,
- arg_resume_device,
+ device,
+ device,
arg_resume_options ?: arg_root_options,
NULL);
if (r < 0)
- return r;
+ log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
- return 0;
+ r = write_drop_in_format(arg_dest, SPECIAL_HIBERNATE_RESUME_SERVICE, 90, "device-dependency",
+ "# Automatically generated by systemd-hibernate-resume-generator\n\n"
+ "[Unit]\n"
+ "BindsTo=%1$s\n"
+ "After=%1$s\n",
+ device_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write device dependency drop-in: %m");
+
+ return generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE);
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ _cleanup_(hibernate_info_done) HibernateInfo info = {};
int r;
arg_dest = ASSERT_PTR(dest);
/* Don't even consider resuming outside of initrd. */
if (!in_initrd()) {
- log_debug("Not running in an initrd, exiting.");
+ log_debug("Not running in initrd, exiting.");
return 0;
}
@@ -279,20 +108,19 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (arg_noresume) {
- log_info("Found \"noresume\" on the kernel command line, exiting.");
+ log_info("Found 'noresume' on the kernel command line, exiting.");
return 0;
}
- if (!arg_resume_device && arg_resume_offset_set)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Found resume_offset=%" PRIu64 " but resume= is unset, refusing.",
- arg_resume_offset);
-
- r = parse_efi_hibernate_location();
- if (r == -ENOMEM)
+ r = acquire_hibernate_info(&info);
+ if (r == -ENODEV) {
+ log_debug_errno(r, "No resume device found, exiting.");
+ return 0;
+ }
+ if (r < 0)
return r;
- return process_resume();
+ return process_resume(info.device);
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c
index 28cab191fc..3c8335e175 100644
--- a/src/hibernate-resume/hibernate-resume.c
+++ b/src/hibernate-resume/hibernate-resume.c
@@ -4,14 +4,33 @@
#include <sys/stat.h>
#include "devnum-util.h"
+#include "hibernate-resume-config.h"
#include "initrd-util.h"
#include "log.h"
#include "main-func.h"
#include "parse-util.h"
#include "sleep-util.h"
+#include "static-destruct.h"
-static const char *arg_resume_device = NULL;
-static uint64_t arg_resume_offset = 0; /* in memory pages */
+HibernateInfo arg_info = {};
+
+STATIC_DESTRUCTOR_REGISTER(arg_info, hibernate_info_done);
+
+static int setup_hibernate_info_and_warn(void) {
+ int r;
+
+ r = acquire_hibernate_info(&arg_info);
+ if (r == -ENODEV) {
+ log_info_errno(r, "No resume device found, exiting.");
+ return 0;
+ }
+ if (r < 0)
+ return r;
+
+ compare_hibernate_location_and_warn(&arg_info);
+
+ return 1;
+}
static int run(int argc, char *argv[]) {
struct stat st;
@@ -19,35 +38,44 @@ static int run(int argc, char *argv[]) {
log_setup();
- if (argc < 2 || argc > 3)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects one or two arguments.");
+ if (argc < 1 || argc > 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects zero, one, or two arguments.");
umask(0022);
if (!in_initrd())
return 0;
- arg_resume_device = argv[1];
+ if (argc > 1) {
+ arg_info.device = argv[1];
+
+ if (argc == 3) {
+ r = safe_atou64(argv[2], &arg_info.offset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[2]);
+ }
+ } else {
+ r = setup_hibernate_info_and_warn();
+ if (r <= 0)
+ return r;
- if (argc == 3) {
- r = safe_atou64(argv[2], &arg_resume_offset);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[2]);
+ if (arg_info.from_efi)
+ clear_efi_hibernate_location();
}
- if (stat(arg_resume_device, &st) < 0)
- return log_error_errno(errno, "Failed to stat resume device '%s': %m", arg_resume_device);
+ if (stat(arg_info.device, &st) < 0)
+ return log_error_errno(errno, "Failed to stat resume device '%s': %m", arg_info.device);
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Resume device '%s' is not a block device.", arg_resume_device);
+ "Resume device '%s' is not a block device.", arg_info.device);
/* The write shall not return if a resume takes place. */
- r = write_resume_config(st.st_rdev, arg_resume_offset, arg_resume_device);
+ r = write_resume_config(st.st_rdev, arg_info.offset, arg_info.device);
log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG,
r < 0 ? r : SYNTHETIC_ERRNO(ENOENT),
"Unable to resume from device '%s' (" DEVNUM_FORMAT_STR ") offset %" PRIu64 ", continuing boot process.",
- arg_resume_device, DEVNUM_FORMAT_VAL(st.st_rdev), arg_resume_offset);
+ arg_info.device, DEVNUM_FORMAT_VAL(st.st_rdev), arg_info.offset);
return r;
}
diff --git a/src/hibernate-resume/meson.build b/src/hibernate-resume/meson.build
index b14ea356c5..4a6430cc49 100644
--- a/src/hibernate-resume/meson.build
+++ b/src/hibernate-resume/meson.build
@@ -4,11 +4,11 @@ executables += [
generator_template + {
'name' : 'systemd-hibernate-resume-generator',
'conditions' : ['ENABLE_HIBERNATE'],
- 'sources' : files('hibernate-resume-generator.c'),
+ 'sources' : files('hibernate-resume-generator.c', 'hibernate-resume-config.c'),
},
libexec_template + {
'name' : 'systemd-hibernate-resume',
'conditions' : ['ENABLE_HIBERNATE'],
- 'sources' : files('hibernate-resume.c'),
+ 'sources' : files('hibernate-resume.c', 'hibernate-resume-config.c'),
},
]
diff --git a/units/meson.build b/units/meson.build
index 2e649d6e5f..2fb87934b8 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -292,6 +292,10 @@ units = [
{ 'file' : 'systemd-growfs@.service.in' },
{ 'file' : 'systemd-halt.service' },
{
+ 'file' : 'systemd-hibernate-resume.service.in',
+ 'conditions' : ['ENABLE_HIBERNATE'],
+ },
+ {
'file' : 'systemd-hibernate.service.in',
'conditions' : ['ENABLE_HIBERNATE'],
},
diff --git a/units/systemd-hibernate-resume.service.in b/units/systemd-hibernate-resume.service.in
new file mode 100644
index 0000000000..dce4f0ffe1
--- /dev/null
+++ b/units/systemd-hibernate-resume.service.in
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Resume from hibernation
+Documentation=man:systemd-hibernate-resume.service(8)
+
+ConditionKernelCommandLine=!noresume
+
+DefaultDependencies=no
+Wants=local-fs-pre.target
+Before=local-fs-pre.target
+
+AssertPathExists=/etc/initrd-release
+
+[Service]
+Type=oneshot
+ExecStart={{LIBEXECDIR}}/systemd-hibernate-resume