1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "alloc-util.h"
6 #include "dropin.h"
7 #include "generator.h"
8 #include "mkdir-label.h"
9 #include "parse-util.h"
10 #include "path-util.h"
11 #include "proc-cmdline.h"
12 #include "special.h"
13 #include "string-util.h"
14 #include "strv.h"
15 #include "unit-file.h"
16 #include "unit-name.h"
17
18 static const char *arg_dest = NULL;
19 static char *arg_default_unit = NULL;
20 static char **arg_mask = NULL;
21 static char **arg_wants = NULL;
22 static char *arg_debug_shell = NULL;
23
24 STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
25 STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
26 STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
27 STATIC_DESTRUCTOR_REGISTER(arg_debug_shell, 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 int r;
31
32 assert(key);
33
34 if (streq(key, "systemd.mask")) {
35 char *n;
36
37 if (proc_cmdline_value_missing(key, value))
38 return 0;
39
40 r = unit_name_mangle(value, UNIT_NAME_MANGLE_WARN, &n);
41 if (r < 0)
42 return log_error_errno(r, "Failed to glob unit name: %m");
43
44 r = strv_consume(&arg_mask, n);
45 if (r < 0)
46 return log_oom();
47
48 } else if (streq(key, "systemd.wants")) {
49 char *n;
50
51 if (proc_cmdline_value_missing(key, value))
52 return 0;
53
54 r = unit_name_mangle(value, UNIT_NAME_MANGLE_WARN, &n);
55 if (r < 0)
56 return log_error_errno(r, "Failed to glob unit name: %m");
57
58 r = strv_consume(&arg_wants, n);
59 if (r < 0)
60 return log_oom();
61
62 } else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
63 const char *t = NULL;
64
65 r = value ? parse_boolean(value) : 1;
66 if (r < 0)
67 t = skip_dev_prefix(value);
68 else if (r > 0)
69 t = skip_dev_prefix(DEBUGTTY);
70
71 return free_and_strdup_warn(&arg_debug_shell, t);
72
73 } else if (streq(key, "systemd.unit")) {
74
75 if (proc_cmdline_value_missing(key, value))
76 return 0;
77
78 return free_and_strdup_warn(&arg_default_unit, value);
79
80 } else if (!value) {
81 const char *target;
82
83 target = runlevel_to_target(key);
84 if (target)
85 return free_and_strdup_warn(&arg_default_unit, target);
86 }
87
88 return 0;
89 }
90
generate_mask_symlinks(void)91 static int generate_mask_symlinks(void) {
92 int r = 0;
93
94 if (strv_isempty(arg_mask))
95 return 0;
96
97 STRV_FOREACH(u, arg_mask) {
98 _cleanup_free_ char *p = NULL;
99
100 p = path_join(empty_to_root(arg_dest), *u);
101 if (!p)
102 return log_oom();
103
104 if (symlink("/dev/null", p) < 0)
105 r = log_error_errno(errno,
106 "Failed to create mask symlink %s: %m",
107 p);
108 }
109
110 return r;
111 }
112
generate_wants_symlinks(void)113 static int generate_wants_symlinks(void) {
114 int r = 0;
115
116 if (strv_isempty(arg_wants))
117 return 0;
118
119 STRV_FOREACH(u, arg_wants) {
120 _cleanup_free_ char *p = NULL, *f = NULL;
121 const char *target;
122
123 /* This should match what do_queue_default_job() in core/main.c does. */
124 if (arg_default_unit)
125 target = arg_default_unit;
126 else if (in_initrd())
127 target = SPECIAL_INITRD_TARGET;
128 else
129 target = SPECIAL_DEFAULT_TARGET;
130
131 p = strjoin(arg_dest, "/", target, ".wants/", *u);
132 if (!p)
133 return log_oom();
134
135 f = path_join(SYSTEM_DATA_UNIT_DIR, *u);
136 if (!f)
137 return log_oom();
138
139 (void) mkdir_parents_label(p, 0755);
140
141 if (symlink(f, p) < 0)
142 r = log_error_errno(errno,
143 "Failed to create wants symlink %s: %m",
144 p);
145 }
146
147 return r;
148 }
149
install_debug_shell_dropin(const char * dir)150 static void install_debug_shell_dropin(const char *dir) {
151 int r;
152
153 if (streq(arg_debug_shell, skip_dev_prefix(DEBUGTTY)))
154 return;
155
156 r = write_drop_in_format(dir, "debug-shell.service", 50, "tty",
157 "[Unit]\n"
158 "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
159 "ConditionPathExists=\n"
160 "[Service]\n"
161 "TTYPath=/dev/%s",
162 arg_debug_shell, arg_debug_shell);
163 if (r < 0)
164 log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
165 }
166
run(const char * dest,const char * dest_early,const char * dest_late)167 static int run(const char *dest, const char *dest_early, const char *dest_late) {
168 int r, q;
169
170 assert_se(arg_dest = dest_early);
171
172 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_RD_STRICT | PROC_CMDLINE_STRIP_RD_PREFIX);
173 if (r < 0)
174 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
175
176 if (arg_debug_shell) {
177 r = strv_extend(&arg_wants, "debug-shell.service");
178 if (r < 0)
179 return log_oom();
180
181 install_debug_shell_dropin(arg_dest);
182 }
183
184 r = generate_mask_symlinks();
185 q = generate_wants_symlinks();
186
187 return r < 0 ? r : q;
188 }
189
190 DEFINE_MAIN_GENERATOR_FUNCTION(run);
191