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