]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hibernate-resume/hibernate-resume-generator.c
udev: gracefully handle ENODEV or friends in opening device node
[thirdparty/systemd.git] / src / hibernate-resume / hibernate-resume-generator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d2c68822 2
b5efdb8a 3#include "alloc-util.h"
ff757c9d 4#include "dropin.h"
656a7bb1
MY
5#include "efi-loader.h"
6#include "fd-util.h"
7#include "fileio.h"
afe44c8f 8#include "generator.h"
a628d933 9#include "hibernate-resume-config.h"
baa6a42d 10#include "initrd-util.h"
d2c68822 11#include "log.h"
4e731273 12#include "proc-cmdline.h"
07630cea 13#include "special.h"
a628d933 14#include "static-destruct.h"
07630cea 15#include "string-util.h"
d2c68822
IS
16#include "unit-name.h"
17
b8110a3e 18static const char *arg_dest = NULL;
656a7bb1 19static const char *arg_dest_late = NULL;
8b6805a2 20static char *arg_resume_options = NULL;
70e843fe 21static char *arg_root_options = NULL;
e83419d0 22static bool arg_noresume = false;
d2c68822 23
8b6805a2 24STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
70e843fe 25STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
77182cc6 26
96287a49 27static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
a8d3315b
YW
28 assert(key);
29
a628d933 30 if (streq(key, "resumeflags")) {
8b6805a2
JR
31
32 if (proc_cmdline_value_missing(key, value))
33 return 0;
34
c2bc710b 35 if (!strextend_with_separator(&arg_resume_options, ",", value))
8b6805a2
JR
36 return log_oom();
37
a8d3315b 38 } else if (streq(key, "rootflags")) {
70e843fe
JR
39
40 if (proc_cmdline_value_missing(key, value))
41 return 0;
42
c2bc710b 43 if (!strextend_with_separator(&arg_root_options, ",", value))
70e843fe
JR
44 return log_oom();
45
a8d3315b
YW
46 } else if (streq(key, "noresume")) {
47
e83419d0 48 if (value) {
a628d933 49 log_warning("'noresume' kernel command line option specified with an argument, ignoring.");
e83419d0
ZJS
50 return 0;
51 }
52
53 arg_noresume = true;
d2c68822
IS
54 }
55
56 return 0;
57}
58
656a7bb1
MY
59#define DISSECTED_SWAP_LUKS_DEVICE "/dev/disk/by-designator/swap-luks"
60#define DISSECTED_SWAP_LUKS_DEVICE_UNIT "dev-disk-by\\x2ddesignator-swap\\x2dluks.device"
61
62static int add_dissected_swap_cryptsetup(void) {
63
64#if HAVE_LIBCRYPTSETUP
65 _cleanup_fclose_ FILE *f = NULL;
66 int r;
67
68 /* Write out cryptsetup unit for the "auto" swap device (/dev/disk/by-designator/swap-luks), so that
69 * resume from hibernation can be automatically initiated there. This mostly follows what gpt-auto does,
70 * but operates in initrd. */
71
72 r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, "systemd-cryptsetup@swap.service", &f);
73 if (r < 0)
74 return r;
75
76 r = generator_write_cryptsetup_unit_section(f, /* source = */ NULL);
77 if (r < 0)
78 return r;
79
80 fputs("Before=umount.target cryptsetup.target\n"
81 "Conflicts=umount.target\n"
82 "BindsTo="DISSECTED_SWAP_LUKS_DEVICE_UNIT"\n"
83 "After="DISSECTED_SWAP_LUKS_DEVICE_UNIT"\n",
84 f);
85
86 r = generator_write_cryptsetup_service_section(
87 f, "swap", DISSECTED_SWAP_LUKS_DEVICE,
88 /* key_file = */ NULL,
89 efi_measured_uki(LOG_DEBUG) > 0 ? "tpm2-device=auto" : NULL);
90 if (r < 0)
91 return r;
92
93 r = fflush_and_check(f);
94 if (r < 0)
95 return log_error_errno(r, "Failed to write cryptsetup unit for " DISSECTED_SWAP_LUKS_DEVICE ": %m");
96
97 r = generator_write_device_timeout(arg_dest_late,
98 DISSECTED_SWAP_LUKS_DEVICE,
99 arg_resume_options ?: arg_root_options, /* filtered = */ NULL);
100 if (r < 0)
101 return r;
102
103 return generator_add_symlink(arg_dest_late, DISSECTED_SWAP_LUKS_DEVICE_UNIT, "wants", "systemd-cryptsetup@swap.service");
104#else
105 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
106 "systemd-hibernate-resume-generator was compiled without libcryptsetup support, "
107 "not generating cryptsetup unit for " DISSECTED_SWAP_LUKS_DEVICE ".");
108#endif
109}
110
6cfce71b 111static int process_resume(const HibernateInfo *info) {
a628d933 112 _cleanup_free_ char *device_unit = NULL;
7410616c 113 int r;
d2c68822 114
6cfce71b 115 assert(info);
b5884878 116
6cfce71b 117 r = unit_name_from_path(info->device, ".device", &device_unit);
ff757c9d 118 if (r < 0)
6cfce71b 119 return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", info->device);
ff757c9d 120
9a097ece 121 r = generator_write_device_timeout(arg_dest, info->device, arg_resume_options ?: arg_root_options, NULL);
70e843fe 122 if (r < 0)
a628d933 123 log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
a46f36de
MY
124 if (r <= 0) {
125 /* No timeout explicitly defined? Wait infinitely if resume= is specified, 2min if from EFI
126 * HibernateLocation variable. In the latter case, we avoid blocking the boot process forever
127 * if a stale var is detected while the swap device is not present. */
128 r = write_drop_in_format(arg_dest, device_unit, 40, "device-timeout",
129 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
130 "[Unit]\n"
131 "JobTimeoutSec=%s\n",
132 info->cmdline ? "infinity" : "2min");
133 if (r < 0)
134 log_warning_errno(r, "Failed to write fallback device timeout drop-in, ignoring: %m");
135 }
70e843fe 136
a628d933
MY
137 r = write_drop_in_format(arg_dest, SPECIAL_HIBERNATE_RESUME_SERVICE, 90, "device-dependency",
138 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
139 "[Unit]\n"
140 "BindsTo=%1$s\n"
141 "After=%1$s\n",
142 device_unit);
143 if (r < 0)
144 return log_error_errno(r, "Failed to write device dependency drop-in: %m");
145
656a7bb1
MY
146 /* Generate cryptsetup unit for /dev/disk/by-designator/swap-luks if we hibernated into it, but only
147 * if resume= is not specified, on the assumption that the user would have everything configured
148 * manually otherwise. */
149 if (!info->cmdline && info->efi->auto_swap)
150 (void) add_dissected_swap_cryptsetup();
151
a628d933 152 return generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE);
d2c68822
IS
153}
154
b8110a3e 155static int run(const char *dest, const char *dest_early, const char *dest_late) {
a628d933 156 _cleanup_(hibernate_info_done) HibernateInfo info = {};
760e99bb 157 int r;
d2c68822 158
b8110a3e 159 arg_dest = ASSERT_PTR(dest);
656a7bb1 160 arg_dest_late = ASSERT_PTR(dest_late);
d2c68822 161
32e27670 162 /* Don't even consider resuming outside of initrd. */
a79858bf 163 if (!in_initrd()) {
a628d933 164 log_debug("Not running in initrd, exiting.");
77182cc6 165 return 0;
a79858bf 166 }
d2c68822 167
70ae9dc4
MY
168 if (generator_soft_rebooted()) {
169 log_debug("Running in an initrd entered through soft-reboot, not initiating resume.");
170 return 0;
171 }
172
1d84ad94 173 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
b5884878 174 if (r < 0)
da927ba9 175 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
d2c68822 176
e83419d0 177 if (arg_noresume) {
a628d933 178 log_info("Found 'noresume' on the kernel command line, exiting.");
77182cc6 179 return 0;
e83419d0
ZJS
180 }
181
a628d933
MY
182 r = acquire_hibernate_info(&info);
183 if (r == -ENODEV) {
184 log_debug_errno(r, "No resume device found, exiting.");
185 return 0;
186 }
187 if (r < 0)
9deeca12
MY
188 return r;
189
6cfce71b 190 return process_resume(&info);
d2c68822 191}
77182cc6 192
b8110a3e 193DEFINE_MAIN_GENERATOR_FUNCTION(run);