Merge pull request #29036 from YHNdnzj/hibernate-resume-when-battery-low
hibernate-resume: split out the logic of finding hibernate location and support battery-check better
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -16,7 +16,7 @@ ConditionKernelCommandLine=!systemd.battery-check=0
|
||||
AssertPathExists=/etc/initrd-release
|
||||
DefaultDependencies=no
|
||||
After=plymouth-start.service
|
||||
Before=initrd-root-device.target
|
||||
Before=initrd-root-device.target systemd-hibernate-resume.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user