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