mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 20:23:36 +08:00
Merge pull request #32043 from YHNdnzj/resume-clear-efi
units: introduce systemd-hibernate-clear.service that clears stale HibernateLocation EFI variable
This commit is contained in:
commit
3a6bee0510
@ -590,10 +590,9 @@
|
||||
<term><varname>resumeflags=</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Enables resume from hibernation using the specified
|
||||
device and mount options. All
|
||||
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-like
|
||||
paths are supported. For details, see
|
||||
<para>Enable resume from hibernation using the specified device and timeout options. All
|
||||
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-style
|
||||
device identifiers are supported. For details, see
|
||||
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v217"/>
|
||||
|
@ -944,7 +944,7 @@ manpages = [
|
||||
['systemd-hibernate-resume-generator', '8', [], 'ENABLE_HIBERNATE'],
|
||||
['systemd-hibernate-resume.service',
|
||||
'8',
|
||||
['systemd-hibernate-resume'],
|
||||
['systemd-hibernate-resume', 'systemd-hibernate-clear.service'],
|
||||
'ENABLE_HIBERNATE'],
|
||||
['systemd-homed.service', '8', ['systemd-homed'], 'ENABLE_HOMED'],
|
||||
['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
|
||||
|
@ -17,12 +17,14 @@
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-hibernate-resume.service</refname>
|
||||
<refname>systemd-hibernate-clear.service</refname>
|
||||
<refname>systemd-hibernate-resume</refname>
|
||||
<refpurpose>Resume from hibernation</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-hibernate-resume.service</filename></para>
|
||||
<para><filename>systemd-hibernate-clear.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-hibernate-resume</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -37,6 +39,12 @@
|
||||
<filename>/sys/power/resume</filename>, along with the offset in memory pages
|
||||
(<filename>/sys/power/resume_offset</filename>) if supported.</para>
|
||||
|
||||
<para>The resume device node is either passed directly through arguments, or automatically acquired
|
||||
from kernel command line options and/or <varname>HibernateLocation</varname> EFI variable. The latter
|
||||
will normally be cleared by <filename>systemd-hibernate-resume.service</filename> on resumption.
|
||||
If a stale variable is detected, it would be cleared by
|
||||
<filename>systemd-hibernate-clear.service</filename>.</para>
|
||||
|
||||
<para>Failing to initiate a resume is not an error condition. It may mean that there was
|
||||
no resume image (e. g. if the system has been simply powered off and not hibernated).
|
||||
In such cases, the boot is ordinarily continued.</para>
|
||||
|
@ -28,20 +28,7 @@ static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLo
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
|
||||
|
||||
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;
|
||||
|
||||
static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
|
||||
EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
@ -55,8 +42,6 @@ static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e
|
||||
return mfree(e);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
|
||||
|
||||
void hibernate_info_done(HibernateInfo *info) {
|
||||
assert(info);
|
||||
|
||||
@ -140,7 +125,7 @@ static bool validate_efi_hibernate_location(EFIHibernateLocation *e) {
|
||||
|
||||
if (!streq_ptr(id, e->id) ||
|
||||
!streq_ptr(image_id, e->image_id)) {
|
||||
log_notice("HibernateLocation system identifier doesn't match currently running system, not resuming from it.");
|
||||
log_notice("HibernateLocation system identifier doesn't match currently running system, would not resume from it.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -152,8 +137,9 @@ static bool validate_efi_hibernate_location(EFIHibernateLocation *e) {
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
|
||||
int get_efi_hibernate_location(EFIHibernateLocation **ret) {
|
||||
|
||||
static const JsonDispatch dispatch_table[] = {
|
||||
{ "uuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), JSON_MANDATORY },
|
||||
@ -171,8 +157,6 @@ static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
|
||||
_cleanup_free_ char *location_str = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (!is_efi_boot())
|
||||
goto skip;
|
||||
|
||||
@ -211,15 +195,18 @@ static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
|
||||
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);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(e);
|
||||
return 1;
|
||||
|
||||
skip:
|
||||
*ret = NULL;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void compare_hibernate_location_and_warn(const HibernateInfo *info) {
|
||||
#if ENABLE_EFI
|
||||
int r;
|
||||
|
||||
assert(info);
|
||||
@ -243,8 +230,8 @@ void compare_hibernate_location_and_warn(const HibernateInfo *info) {
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int acquire_hibernate_info(HibernateInfo *ret) {
|
||||
_cleanup_(hibernate_info_done) HibernateInfo i = {};
|
||||
@ -254,11 +241,9 @@ int acquire_hibernate_info(HibernateInfo *ret) {
|
||||
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;
|
||||
|
@ -5,8 +5,27 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct KernelHibernateLocation KernelHibernateLocation;
|
||||
typedef struct EFIHibernateLocation EFIHibernateLocation;
|
||||
|
||||
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;
|
||||
|
||||
EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
|
||||
|
||||
int get_efi_hibernate_location(EFIHibernateLocation **ret);
|
||||
|
||||
typedef struct HibernateInfo {
|
||||
const char *device;
|
||||
@ -20,14 +39,4 @@ void hibernate_info_done(HibernateInfo *info);
|
||||
|
||||
int acquire_hibernate_info(HibernateInfo *ret);
|
||||
|
||||
#if ENABLE_EFI
|
||||
|
||||
void compare_hibernate_location_and_warn(const HibernateInfo *info);
|
||||
|
||||
#else
|
||||
|
||||
static inline void compare_hibernate_location_and_warn(const HibernateInfo *info) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,8 +1,10 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "build.h"
|
||||
#include "devnum-util.h"
|
||||
#include "hibernate-resume-config.h"
|
||||
#include "hibernate-util.h"
|
||||
@ -10,12 +12,84 @@
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "static-destruct.h"
|
||||
#include "terminal-util.h"
|
||||
|
||||
static HibernateInfo arg_info = {};
|
||||
static bool arg_clear = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_info, hibernate_info_done);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-hibernate-resume", "8", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] [DEVICE [OFFSET]]\n"
|
||||
"\n%sInitiate resume from hibernation.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --clear Clear hibernation storage information from EFI and exit\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_CLEAR,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "clear", no_argument, NULL, ARG_CLEAR },
|
||||
{}
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_CLEAR:
|
||||
arg_clear = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (argc > optind && arg_clear)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Extraneous arguments specified with --clear, refusing.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setup_hibernate_info_and_warn(void) {
|
||||
int r;
|
||||
|
||||
@ -32,42 +106,68 @@ static int setup_hibernate_info_and_warn(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int action_clear(void) {
|
||||
int r;
|
||||
|
||||
assert(arg_clear);
|
||||
|
||||
/* Let's insist that the system identifier is verified still. After all if things don't match,
|
||||
* the resume wouldn't get triggered in the first place. We should not erase the var if booted
|
||||
* from LiveCD/portable systems/... */
|
||||
r = get_efi_hibernate_location(/* ret = */ NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = clear_efi_hibernate_location_and_warn();
|
||||
if (r > 0)
|
||||
log_notice("Successfully cleared HibernateLocation EFI variable.");
|
||||
return r;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
if (argc < 1 || argc > 3)
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (argc - optind > 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects zero, one, or two arguments.");
|
||||
|
||||
umask(0022);
|
||||
|
||||
if (arg_clear)
|
||||
return action_clear();
|
||||
|
||||
if (!in_initrd())
|
||||
return 0;
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Not running in initrd, refusing to initiate resume from hibernation.");
|
||||
|
||||
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 {
|
||||
if (argc <= optind) {
|
||||
r = setup_hibernate_info_and_warn();
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_info.efi)
|
||||
clear_efi_hibernate_location_and_warn();
|
||||
(void) clear_efi_hibernate_location_and_warn();
|
||||
} else {
|
||||
arg_info.device = ASSERT_PTR(argv[optind]);
|
||||
|
||||
if (argc - optind == 2) {
|
||||
r = safe_atou64(argv[optind + 1], &arg_info.offset);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[optind + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
|
||||
"Resume device '%s' is not a block device.", arg_info.device);
|
||||
|
||||
/* The write shall not return if a resume takes place. */
|
||||
|
@ -513,13 +513,17 @@ int write_resume_config(dev_t devno, uint64_t offset, const char *device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear_efi_hibernate_location_and_warn(void) {
|
||||
int clear_efi_hibernate_location_and_warn(void) {
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot())
|
||||
return;
|
||||
return 0;
|
||||
|
||||
r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
|
||||
return log_warning_errno(r, "Failed to clear EFI variable HibernateLocation: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ int hibernation_is_safe(void);
|
||||
|
||||
int write_resume_config(dev_t devno, uint64_t offset, const char *device);
|
||||
|
||||
void clear_efi_hibernate_location_and_warn(void);
|
||||
int clear_efi_hibernate_location_and_warn(void);
|
||||
|
||||
/* Only for test-fiemap */
|
||||
int read_fiemap(int fd, struct fiemap **ret);
|
||||
|
@ -308,7 +308,7 @@ static int execute(
|
||||
|
||||
fail:
|
||||
if (SLEEP_OPERATION_IS_HIBERNATION(operation))
|
||||
clear_efi_hibernate_location_and_warn();
|
||||
(void) clear_efi_hibernate_location_and_warn();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -312,6 +312,11 @@ units = [
|
||||
{ 'file' : 'systemd-growfs-root.service.in' },
|
||||
{ 'file' : 'systemd-growfs@.service.in' },
|
||||
{ 'file' : 'systemd-halt.service' },
|
||||
{
|
||||
'file' : 'systemd-hibernate-clear.service.in',
|
||||
'conditions' : ['ENABLE_HIBERNATE', 'ENABLE_EFI'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-hibernate-resume.service.in',
|
||||
'conditions' : ['ENABLE_HIBERNATE'],
|
||||
|
24
units/systemd-hibernate-clear.service.in
Normal file
24
units/systemd-hibernate-clear.service.in
Normal 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=Clear Stale Hibernate Storage Info
|
||||
Documentation=man:systemd-hibernate-clear.service(8)
|
||||
|
||||
ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
|
||||
DefaultDependencies=no
|
||||
Before=sysinit.target shutdown.target
|
||||
Conflicts=shutdown.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart={{LIBEXECDIR}}/systemd-hibernate-resume --clear
|
Loading…
Reference in New Issue
Block a user