1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <unistd.h>
6
7 #include "alloc-util.h"
8 #include "dropin.h"
9 #include "fstab-util.h"
10 #include "generator.h"
11 #include "log.h"
12 #include "main-func.h"
13 #include "mkdir-label.h"
14 #include "proc-cmdline.h"
15 #include "special.h"
16 #include "string-util.h"
17 #include "unit-name.h"
18
19 static const char *arg_dest = "/tmp";
20 static char *arg_resume_device = NULL;
21 static char *arg_resume_options = NULL;
22 static char *arg_root_options = NULL;
23 static bool arg_noresume = false;
24
25 STATIC_DESTRUCTOR_REGISTER(arg_resume_device, freep);
26 STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
27 STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
28
parse_proc_cmdline_item(const char * key,const char * value,void * data)29 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
30
31 if (streq(key, "resume")) {
32 char *s;
33
34 if (proc_cmdline_value_missing(key, value))
35 return 0;
36
37 s = fstab_node_to_udev_node(value);
38 if (!s)
39 return log_oom();
40
41 free_and_replace(arg_resume_device, s);
42
43 } else if (streq(key, "resumeflags")) {
44
45 if (proc_cmdline_value_missing(key, value))
46 return 0;
47
48 if (!strextend_with_separator(&arg_resume_options, ",", value))
49 return log_oom();
50
51 } else if (streq(key, "rootflags")) {
52
53 if (proc_cmdline_value_missing(key, value))
54 return 0;
55
56 if (!strextend_with_separator(&arg_root_options, ",", value))
57 return log_oom();
58
59 } else if (streq(key, "noresume")) {
60 if (value) {
61 log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
62 return 0;
63 }
64
65 arg_noresume = true;
66 }
67
68 return 0;
69 }
70
process_resume(void)71 static int process_resume(void) {
72 _cleanup_free_ char *service_unit = NULL, *device_unit = NULL, *lnk = NULL;
73 int r;
74
75 if (!arg_resume_device)
76 return 0;
77
78 r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service",
79 &service_unit);
80 if (r < 0)
81 return log_error_errno(r, "Failed to generate unit name: %m");
82
83 lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", service_unit);
84 if (!lnk)
85 return log_oom();
86
87 (void) mkdir_parents_label(lnk, 0755);
88 if (symlink(SYSTEM_DATA_UNIT_DIR "/systemd-hibernate-resume@.service", lnk) < 0)
89 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
90
91 r = unit_name_from_path(arg_resume_device, ".device", &device_unit);
92 if (r < 0)
93 return log_error_errno(r, "Failed to generate unit name: %m");
94
95 r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
96 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
97 "[Unit]\nJobTimeoutSec=0");
98 if (r < 0)
99 log_warning_errno(r, "Failed to write device timeout drop-in: %m");
100
101 r = generator_write_timeouts(arg_dest,
102 arg_resume_device,
103 arg_resume_device,
104 arg_resume_options ?: arg_root_options,
105 NULL);
106 if (r < 0)
107 return r;
108
109 return 0;
110 }
111
run(int argc,char * argv[])112 static int run(int argc, char *argv[]) {
113 int r = 0;
114
115 log_setup_generator();
116
117 if (argc > 1 && argc != 4)
118 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
119 "This program takes three or no arguments.");
120
121 if (argc > 1)
122 arg_dest = argv[1];
123
124 /* Don't even consider resuming outside of initramfs. */
125 if (!in_initrd()) {
126 log_debug("Not running in an initrd, quitting.");
127 return 0;
128 }
129
130 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
131 if (r < 0)
132 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
133
134 if (arg_noresume) {
135 log_notice("Found \"noresume\" on the kernel command line, quitting.");
136 return 0;
137 }
138
139 return process_resume();
140 }
141
142 DEFINE_MAIN_FUNCTION(run);
143