1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
8 #include "mkdir-label.h"
9 #include "parse-util.h"
10 #include "path-util.h"
11 #include "proc-cmdline.h"
13 #include "string-util.h"
15 #include "unit-file.h"
16 #include "unit-name.h"
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
;
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
);
29 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
34 if (streq(key
, "systemd.mask")) {
37 if (proc_cmdline_value_missing(key
, value
))
40 r
= unit_name_mangle(value
, UNIT_NAME_MANGLE_WARN
, &n
);
42 return log_error_errno(r
, "Failed to glob unit name: %m");
44 r
= strv_consume(&arg_mask
, n
);
48 } else if (streq(key
, "systemd.wants")) {
51 if (proc_cmdline_value_missing(key
, value
))
54 r
= unit_name_mangle(value
, UNIT_NAME_MANGLE_WARN
, &n
);
56 return log_error_errno(r
, "Failed to glob unit name: %m");
58 r
= strv_consume(&arg_wants
, n
);
62 } else if (proc_cmdline_key_streq(key
, "systemd.debug_shell")) {
65 r
= value
? parse_boolean(value
) : 1;
67 t
= skip_dev_prefix(value
);
69 t
= skip_dev_prefix(DEBUGTTY
);
71 return free_and_strdup_warn(&arg_debug_shell
, t
);
73 } else if (streq(key
, "systemd.unit")) {
75 if (proc_cmdline_value_missing(key
, value
))
78 return free_and_strdup_warn(&arg_default_unit
, value
);
83 target
= runlevel_to_target(key
);
85 return free_and_strdup_warn(&arg_default_unit
, target
);
91 static int generate_mask_symlinks(void) {
95 if (strv_isempty(arg_mask
))
98 STRV_FOREACH(u
, arg_mask
) {
99 _cleanup_free_
char *p
= NULL
;
101 p
= path_join(empty_to_root(arg_dest
), *u
);
105 if (symlink("/dev/null", p
) < 0)
106 r
= log_error_errno(errno
,
107 "Failed to create mask symlink %s: %m",
114 static int generate_wants_symlinks(void) {
118 if (strv_isempty(arg_wants
))
121 STRV_FOREACH(u
, arg_wants
) {
122 _cleanup_free_
char *p
= NULL
, *f
= NULL
;
125 /* This should match what do_queue_default_job() in core/main.c does. */
126 if (arg_default_unit
)
127 target
= arg_default_unit
;
128 else if (in_initrd())
129 target
= SPECIAL_INITRD_TARGET
;
131 target
= SPECIAL_DEFAULT_TARGET
;
133 p
= strjoin(arg_dest
, "/", target
, ".wants/", *u
);
137 f
= path_join(SYSTEM_DATA_UNIT_DIR
, *u
);
141 (void) mkdir_parents_label(p
, 0755);
143 if (symlink(f
, p
) < 0)
144 r
= log_error_errno(errno
,
145 "Failed to create wants symlink %s: %m",
152 static void install_debug_shell_dropin(const char *dir
) {
155 if (streq(arg_debug_shell
, skip_dev_prefix(DEBUGTTY
)))
158 r
= write_drop_in_format(dir
, "debug-shell.service", 50, "tty",
160 "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
161 "ConditionPathExists=\n"
164 arg_debug_shell
, arg_debug_shell
);
166 log_warning_errno(r
, "Failed to write drop-in for debug-shell.service, ignoring: %m");
169 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
172 assert_se(arg_dest
= dest_early
);
174 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_RD_STRICT
| PROC_CMDLINE_STRIP_RD_PREFIX
);
176 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
178 if (arg_debug_shell
) {
179 r
= strv_extend(&arg_wants
, "debug-shell.service");
183 install_debug_shell_dropin(arg_dest
);
186 r
= generate_mask_symlinks();
187 q
= generate_wants_symlinks();
189 return r
< 0 ? r
: q
;
192 DEFINE_MAIN_GENERATOR_FUNCTION(run
);