1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
5 #include "efi-loader.h"
9 #include "hibernate-resume-config.h"
10 #include "initrd-util.h"
12 #include "proc-cmdline.h"
14 #include "static-destruct.h"
15 #include "string-util.h"
16 #include "unit-name.h"
18 static const char *arg_dest
= NULL
;
19 static const char *arg_dest_late
= NULL
;
20 static char *arg_resume_options
= NULL
;
21 static char *arg_root_options
= NULL
;
22 static bool arg_noresume
= false;
24 STATIC_DESTRUCTOR_REGISTER(arg_resume_options
, freep
);
25 STATIC_DESTRUCTOR_REGISTER(arg_root_options
, freep
);
27 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
30 if (streq(key
, "resumeflags")) {
32 if (proc_cmdline_value_missing(key
, value
))
35 if (!strextend_with_separator(&arg_resume_options
, ",", value
))
38 } else if (streq(key
, "rootflags")) {
40 if (proc_cmdline_value_missing(key
, value
))
43 if (!strextend_with_separator(&arg_root_options
, ",", value
))
46 } else if (streq(key
, "noresume")) {
49 log_warning("'noresume' kernel command line option specified with an argument, ignoring.");
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"
62 static int add_dissected_swap_cryptsetup(void) {
64 #if HAVE_LIBCRYPTSETUP
65 _cleanup_fclose_
FILE *f
= NULL
;
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. */
72 r
= generator_open_unit_file(arg_dest_late
, /* source = */ NULL
, "systemd-cryptsetup@swap.service", &f
);
76 r
= generator_write_cryptsetup_unit_section(f
, /* source = */ NULL
);
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",
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
);
93 r
= fflush_and_check(f
);
95 return log_error_errno(r
, "Failed to write cryptsetup unit for " DISSECTED_SWAP_LUKS_DEVICE
": %m");
97 r
= generator_write_device_timeout(arg_dest_late
,
98 DISSECTED_SWAP_LUKS_DEVICE
,
99 arg_resume_options
?: arg_root_options
, /* filtered = */ NULL
);
103 return generator_add_symlink(arg_dest_late
, DISSECTED_SWAP_LUKS_DEVICE_UNIT
, "wants", "systemd-cryptsetup@swap.service");
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
".");
111 static int process_resume(const HibernateInfo
*info
) {
112 _cleanup_free_
char *device_unit
= NULL
;
117 r
= unit_name_from_path(info
->device
, ".device", &device_unit
);
119 return log_error_errno(r
, "Failed to generate device unit name from path '%s': %m", info
->device
);
121 r
= generator_write_device_timeout(arg_dest
, info
->device
, arg_resume_options
?: arg_root_options
, NULL
);
123 log_warning_errno(r
, "Failed to write device timeout drop-in, ignoring: %m");
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"
131 "JobTimeoutSec=%s\n",
132 info
->cmdline
? "infinity" : "2min");
134 log_warning_errno(r
, "Failed to write fallback device timeout drop-in, ignoring: %m");
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"
144 return log_error_errno(r
, "Failed to write device dependency drop-in: %m");
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();
152 return generator_add_symlink(arg_dest
, SPECIAL_SYSINIT_TARGET
, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE
);
155 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
156 _cleanup_(hibernate_info_done
) HibernateInfo info
= {};
159 arg_dest
= ASSERT_PTR(dest
);
160 arg_dest_late
= ASSERT_PTR(dest_late
);
162 /* Don't even consider resuming outside of initrd. */
164 log_debug("Not running in initrd, exiting.");
168 if (generator_soft_rebooted()) {
169 log_debug("Running in an initrd entered through soft-reboot, not initiating resume.");
173 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
175 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
178 log_info("Found 'noresume' on the kernel command line, exiting.");
182 r
= acquire_hibernate_info(&info
);
184 log_debug_errno(r
, "No resume device found, exiting.");
190 return process_resume(&info
);
193 DEFINE_MAIN_GENERATOR_FUNCTION(run
);