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.
This commit is contained in:
Mike Yuan 2023-09-05 21:50:04 +08:00
parent 82b7bf8c1c
commit a628d933cc
No known key found for this signature in database
GPG key ID: 417471C0A40F58B3
7 changed files with 423 additions and 220 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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_resume_offset);
if (r < 0)
return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[2]);
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 (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;
}

View file

@ -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'),
},
]

View file

@ -291,6 +291,10 @@ units = [
{ 'file' : 'systemd-growfs-root.service.in' },
{ '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'],

View file

@ -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