]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hibernate-resume/hibernate-resume-generator.c
Drop split-usr and unmerged-usr support
[thirdparty/systemd.git] / src / hibernate-resume / hibernate-resume-generator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d2c68822 2
d2c68822 3#include <errno.h>
07630cea 4#include <stdio.h>
9deeca12 5#include <sys/utsname.h>
ca78ad1d 6#include <unistd.h>
d2c68822 7
9deeca12
MY
8#include "sd-id128.h"
9
b5efdb8a 10#include "alloc-util.h"
02819276 11#include "device-nodes.h"
ff757c9d 12#include "dropin.h"
9deeca12 13#include "efivars.h"
760e99bb
MY
14#include "fd-util.h"
15#include "fileio.h"
6550203e 16#include "fstab-util.h"
afe44c8f 17#include "generator.h"
9deeca12 18#include "id128-util.h"
baa6a42d 19#include "initrd-util.h"
9deeca12 20#include "json.h"
d2c68822 21#include "log.h"
77182cc6 22#include "main-func.h"
9deeca12 23#include "os-util.h"
760e99bb 24#include "parse-util.h"
44271942 25#include "path-util.h"
4e731273 26#include "proc-cmdline.h"
07630cea
LP
27#include "special.h"
28#include "string-util.h"
d2c68822
IS
29#include "unit-name.h"
30
b8110a3e 31static const char *arg_dest = NULL;
1d84ad94 32static char *arg_resume_device = NULL;
8b6805a2 33static char *arg_resume_options = NULL;
70e843fe 34static char *arg_root_options = NULL;
e83419d0 35static bool arg_noresume = false;
760e99bb 36static uint64_t arg_resume_offset = 0;
c089af84 37static bool arg_resume_offset_set = false;
d2c68822 38
77182cc6 39STATIC_DESTRUCTOR_REGISTER(arg_resume_device, freep);
8b6805a2 40STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
70e843fe 41STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
77182cc6 42
9deeca12
MY
43#if ENABLE_EFI
44typedef struct EFIHibernateLocation {
45 sd_id128_t uuid;
46 uint64_t offset;
47 const char *kernel_version;
48 const char *id;
49 const char *image_id;
50 const char *version_id;
51 const char *image_version;
52} EFIHibernateLocation;
53#endif
54
96287a49 55static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
760e99bb 56 int r;
7410616c 57
760e99bb 58 if (proc_cmdline_key_streq(key, "resume")) {
1d84ad94
LP
59 char *s;
60
61 if (proc_cmdline_value_missing(key, value))
62 return 0;
63
64 s = fstab_node_to_udev_node(value);
65 if (!s)
d2c68822 66 return log_oom();
1d84ad94 67
e83419d0
ZJS
68 free_and_replace(arg_resume_device, s);
69
760e99bb
MY
70 } else if (proc_cmdline_key_streq(key, "resume_offset")) {
71
72 if (proc_cmdline_value_missing(key, value))
73 return 0;
74
75 r = safe_atou64(value, &arg_resume_offset);
76 if (r < 0)
77 return log_error_errno(r, "Failed to parse resume_offset=%s: %m", value);
78
c089af84
MY
79 arg_resume_offset_set = true;
80
760e99bb 81 } else if (proc_cmdline_key_streq(key, "resumeflags")) {
8b6805a2
JR
82
83 if (proc_cmdline_value_missing(key, value))
84 return 0;
85
c2bc710b 86 if (!strextend_with_separator(&arg_resume_options, ",", value))
8b6805a2
JR
87 return log_oom();
88
760e99bb 89 } else if (proc_cmdline_key_streq(key, "rootflags")) {
70e843fe
JR
90
91 if (proc_cmdline_value_missing(key, value))
92 return 0;
93
c2bc710b 94 if (!strextend_with_separator(&arg_root_options, ",", value))
70e843fe
JR
95 return log_oom();
96
760e99bb 97 } else if (proc_cmdline_key_streq(key, "noresume")) {
e83419d0
ZJS
98 if (value) {
99 log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
100 return 0;
101 }
102
103 arg_noresume = true;
d2c68822
IS
104 }
105
106 return 0;
107}
108
9deeca12
MY
109static int parse_efi_hibernate_location(void) {
110 int r = 0;
111
112#if ENABLE_EFI
113 static const JsonDispatch dispatch_table[] = {
114 { "uuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), JSON_MANDATORY },
115 { "offset", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), JSON_MANDATORY },
116 { "kernelVersion", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, kernel_version), JSON_PERMISSIVE|JSON_DEBUG },
117 { "osReleaseId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, id), JSON_PERMISSIVE|JSON_DEBUG },
118 { "osReleaseImageId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, image_id), JSON_PERMISSIVE|JSON_DEBUG },
119 { "osReleaseVersionId", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, version_id), JSON_PERMISSIVE|JSON_DEBUG },
120 { "osReleaseImageVersion", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(EFIHibernateLocation, image_version), JSON_PERMISSIVE|JSON_DEBUG },
121 {},
122 };
123
124 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
125 _cleanup_free_ char *location_str = NULL, *device = NULL, *id = NULL, *image_id = NULL,
126 *version_id = NULL, *image_version = NULL;
127 struct utsname uts = {};
128 EFIHibernateLocation location = {};
129
130 r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(HibernateLocation), &location_str);
131 if (r == -ENOENT) {
132 log_debug_errno(r, "EFI variable HibernateLocation is not set, skipping.");
133 return 0;
134 }
135 if (r < 0)
136 return log_error_errno(r, "Failed to get EFI variable HibernateLocation: %m");
137
138 r = json_parse(location_str, 0, &v, NULL, NULL);
139 if (r < 0)
140 return log_error_errno(r, "Failed to parse HibernateLocation JSON object: %m");
141
142 r = json_dispatch(v, dispatch_table, NULL, JSON_LOG, &location);
143 if (r < 0)
144 return r;
145
146 if (uname(&uts) < 0)
147 log_warning_errno(errno, "Failed to get kernel info, ignoring: %m");
148
149 r = parse_os_release(NULL,
150 "ID", &id,
151 "IMAGE_ID", &image_id,
152 "VERSION_ID", &version_id,
153 "IMAGE_VERSION", &image_version);
154 if (r < 0)
155 log_warning_errno(r, "Failed to parse os-release, ignoring: %m");
156
157 if (!streq(uts.release, strempty(location.kernel_version)) ||
158 !streq_ptr(id, location.id) ||
159 !streq_ptr(image_id, location.image_id) ||
160 !streq_ptr(version_id, location.version_id) ||
161 !streq_ptr(image_version, location.image_version)) {
162
163 log_notice("HibernateLocation system info doesn't match with current running system, not resuming from it.");
164 return 0;
165 }
166
167 if (asprintf(&device, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(location.uuid)) < 0)
168 return log_oom();
169
170 if (!arg_resume_device) {
171 arg_resume_device = TAKE_PTR(device);
172 arg_resume_offset = location.offset;
173 } else {
02819276
MY
174 if (!path_equal(arg_resume_device, device)) {
175 r = devnode_same(arg_resume_device, device);
176 if (r < 0)
177 log_debug_errno(r,
178 "Failed to check if resume=%s is the same device as HibernateLocation device '%s', ignoring: %m",
179 arg_resume_device, device);
180 if (r == 0)
181 log_warning("resume=%s doesn't match with HibernateLocation device '%s', proceeding anyway with resume=.",
182 arg_resume_device, device);
183 }
9deeca12
MY
184
185 if (arg_resume_offset != location.offset)
186 log_warning("resume_offset=%" PRIu64 " doesn't match with HibernateLocation offset %" PRIu64 ", proceeding anyway with resume_offset=.",
187 arg_resume_offset, location.offset);
188 }
189
190 r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
191 if (r < 0)
192 log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
193#endif
194
195 return r;
196}
197
d2c68822 198static int process_resume(void) {
760e99bb
MY
199 _cleanup_free_ char *device_unit = NULL;
200 _cleanup_fclose_ FILE *f = NULL;
7410616c 201 int r;
d2c68822 202
1d84ad94 203 if (!arg_resume_device)
b5884878
LP
204 return 0;
205
ff757c9d
ZJS
206 r = unit_name_from_path(arg_resume_device, ".device", &device_unit);
207 if (r < 0)
760e99bb 208 return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", arg_resume_device);
ff757c9d
ZJS
209
210 r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
90198bcb 211 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
a9b837aa
LP
212 "[Unit]\n"
213 "JobTimeoutSec=infinity\n");
ff757c9d 214 if (r < 0)
760e99bb
MY
215 log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
216
217 r = generator_open_unit_file(arg_dest, NULL, SPECIAL_HIBERNATE_RESUME_SERVICE, &f);
218 if (r < 0)
219 return r;
220
221 fprintf(f,
222 "[Unit]\n"
223 "Description=Resume from hibernation\n"
224 "Documentation=man:systemd-hibernate-resume.service(8)\n"
225 "DefaultDependencies=no\n"
226 "BindsTo=%1$s\n"
227 "Wants=local-fs-pre.target\n"
228 "After=%1$s\n"
229 "Before=local-fs-pre.target\n"
230 "AssertPathExists=/etc/initrd-release\n"
231 "\n"
232 "[Service]\n"
233 "Type=oneshot\n"
b0d3095f 234 "ExecStart=" LIBEXECDIR "/systemd-hibernate-resume %2$s %3$" PRIu64 "\n",
760e99bb
MY
235 device_unit,
236 arg_resume_device,
237 arg_resume_offset);
238
239 r = fflush_and_check(f);
240 if (r < 0)
241 return log_error_errno(r, "Failed to create " SPECIAL_HIBERNATE_RESUME_SERVICE ": %m");
242
243 r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE);
244 if (r < 0)
245 return r;
8b6805a2 246
ff757c9d
ZJS
247 r = generator_write_timeouts(arg_dest,
248 arg_resume_device,
249 arg_resume_device,
250 arg_resume_options ?: arg_root_options,
251 NULL);
70e843fe
JR
252 if (r < 0)
253 return r;
254
d2c68822
IS
255 return 0;
256}
257
b8110a3e 258static int run(const char *dest, const char *dest_early, const char *dest_late) {
760e99bb 259 int r;
d2c68822 260
b8110a3e 261 arg_dest = ASSERT_PTR(dest);
d2c68822 262
32e27670 263 /* Don't even consider resuming outside of initrd. */
a79858bf 264 if (!in_initrd()) {
0b20c56e 265 log_debug("Not running in an initrd, exiting.");
77182cc6 266 return 0;
a79858bf 267 }
d2c68822 268
1d84ad94 269 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
b5884878 270 if (r < 0)
da927ba9 271 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
d2c68822 272
e83419d0 273 if (arg_noresume) {
0b20c56e 274 log_info("Found \"noresume\" on the kernel command line, exiting.");
77182cc6 275 return 0;
e83419d0
ZJS
276 }
277
c089af84
MY
278 if (!arg_resume_device && arg_resume_offset_set)
279 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
280 "Found resume_offset=%" PRIu64 " but resume= is unset, refusing.",
281 arg_resume_offset);
282
9deeca12
MY
283 r = parse_efi_hibernate_location();
284 if (r == -ENOMEM)
285 return r;
286
77182cc6 287 return process_resume();
d2c68822 288}
77182cc6 289
b8110a3e 290DEFINE_MAIN_GENERATOR_FUNCTION(run);