1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "creds-util.h"
8 #include "errno-util.h"
10 #include "fileio-label.h"
11 #include "generator.h"
12 #include "initrd-util.h"
13 #include "parse-util.h"
14 #include "path-util.h"
15 #include "proc-cmdline.h"
16 #include "recurse-dir.h"
18 #include "string-util.h"
20 #include "unit-file.h"
21 #include "unit-name.h"
23 static const char *arg_dest
= NULL
;
24 static char *arg_default_unit
= NULL
;
25 static char **arg_mask
= NULL
;
26 static char **arg_wants
= NULL
;
27 static bool arg_debug_shell
= false;
28 static char *arg_debug_tty
= NULL
;
29 static char *arg_default_debug_tty
= NULL
;
31 STATIC_DESTRUCTOR_REGISTER(arg_default_unit
, freep
);
32 STATIC_DESTRUCTOR_REGISTER(arg_mask
, strv_freep
);
33 STATIC_DESTRUCTOR_REGISTER(arg_wants
, strv_freep
);
34 STATIC_DESTRUCTOR_REGISTER(arg_debug_tty
, freep
);
35 STATIC_DESTRUCTOR_REGISTER(arg_default_debug_tty
, freep
);
37 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
42 if (streq(key
, "systemd.mask")) {
45 if (proc_cmdline_value_missing(key
, value
))
48 r
= unit_name_mangle(value
, UNIT_NAME_MANGLE_WARN
, &n
);
50 return log_error_errno(r
, "Failed to glob unit name: %m");
52 r
= strv_consume(&arg_mask
, n
);
56 } else if (streq(key
, "systemd.wants")) {
59 if (proc_cmdline_value_missing(key
, value
))
62 r
= unit_name_mangle(value
, UNIT_NAME_MANGLE_WARN
, &n
);
64 return log_error_errno(r
, "Failed to glob unit name: %m");
66 r
= strv_consume(&arg_wants
, n
);
70 } else if (proc_cmdline_key_streq(key
, "systemd.debug_shell")) {
71 r
= value
? parse_boolean(value
) : 1;
72 arg_debug_shell
= r
!= 0;
76 return free_and_strdup_warn(&arg_debug_tty
, skip_dev_prefix(value
));
78 } else if (proc_cmdline_key_streq(key
, "systemd.default_debug_tty")) {
79 if (proc_cmdline_value_missing(key
, value
))
82 return free_and_strdup_warn(&arg_default_debug_tty
, skip_dev_prefix(value
));
84 } else if (streq(key
, "systemd.unit")) {
86 if (proc_cmdline_value_missing(key
, value
))
89 return free_and_strdup_warn(&arg_default_unit
, value
);
94 target
= runlevel_to_target(key
);
96 return free_and_strdup_warn(&arg_default_unit
, target
);
102 static int generate_mask_symlinks(void) {
105 STRV_FOREACH(u
, arg_mask
) {
106 _cleanup_free_
char *p
= NULL
;
108 p
= path_join(empty_to_root(arg_dest
), *u
);
112 if (symlink("/dev/null", p
) < 0)
113 r
= log_error_errno(errno
,
114 "Failed to create mask symlink %s: %m",
121 static int generate_wants_symlinks(void) {
124 STRV_FOREACH(u
, arg_wants
) {
125 _cleanup_free_
char *f
= NULL
;
128 /* This should match what do_queue_default_job() in core/main.c does. */
129 if (arg_default_unit
)
130 target
= arg_default_unit
;
131 else if (in_initrd())
132 target
= SPECIAL_INITRD_TARGET
;
134 target
= SPECIAL_DEFAULT_TARGET
;
136 f
= path_join(SYSTEM_DATA_UNIT_DIR
, *u
);
140 r
= generator_add_symlink(arg_dest
, target
, "wants", f
);
148 static void install_debug_shell_dropin(void) {
149 const char *tty
= arg_debug_tty
?: arg_default_debug_tty
;
152 if (!tty
|| path_equal(tty
, skip_dev_prefix(DEBUGTTY
)))
155 r
= write_drop_in_format(arg_dest
, "debug-shell.service", 50, "tty",
157 "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
158 "ConditionPathExists=\n"
163 log_warning_errno(r
, "Failed to write drop-in for debug-shell.service, ignoring: %m");
166 static int process_unit_credentials(const char *credentials_dir
) {
169 assert(credentials_dir
);
171 _cleanup_free_ DirectoryEntries
*des
= NULL
;
172 r
= readdir_all_at(AT_FDCWD
, credentials_dir
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
174 return log_error_errno(r
, "Failed to enumerate credentials from credentials directory '%s': %m", credentials_dir
);
176 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
177 _cleanup_free_
void *d
= NULL
;
178 struct dirent
*de
= *i
;
179 const char *unit
, *dropin
;
181 if (de
->d_type
!= DT_REG
)
184 unit
= startswith(de
->d_name
, "systemd.extra-unit.");
185 dropin
= startswith(de
->d_name
, "systemd.unit-dropin.");
187 if (!unit
&& !dropin
)
190 if (!unit_name_is_valid(unit
?: dropin
, UNIT_NAME_ANY
)) {
191 log_warning("Invalid unit name '%s' in credential '%s', ignoring.",
192 unit
?: dropin
, de
->d_name
);
196 r
= read_credential_with_decryption(de
->d_name
, &d
, NULL
);
201 _cleanup_free_
char *p
= NULL
;
203 p
= path_join(arg_dest
, unit
);
207 r
= write_string_file_atomic_label(p
, d
);
209 log_warning_errno(r
, "Failed to write unit file '%s' from credential '%s', ignoring: %m",
214 log_debug("Wrote unit file '%s' from credential '%s'", unit
, de
->d_name
);
217 r
= write_drop_in(arg_dest
, dropin
, 50, "credential", d
);
219 log_warning_errno(r
, "Failed to write drop-in for unit '%s' from credential '%s', ignoring: %m",
224 log_debug("Wrote drop-in for unit '%s' from credential '%s'", dropin
, de
->d_name
);
231 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
232 const char *credentials_dir
;
235 assert_se(arg_dest
= dest_early
);
237 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_RD_STRICT
| PROC_CMDLINE_STRIP_RD_PREFIX
);
239 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
241 if (arg_debug_shell
) {
242 r
= strv_extend(&arg_wants
, "debug-shell.service");
246 install_debug_shell_dropin();
249 if (get_credentials_dir(&credentials_dir
) >= 0)
250 RET_GATHER(r
, process_unit_credentials(credentials_dir
));
252 if (get_encrypted_credentials_dir(&credentials_dir
) >= 0)
253 RET_GATHER(r
, process_unit_credentials(credentials_dir
));
255 RET_GATHER(r
, generate_mask_symlinks());
256 RET_GATHER(r
, generate_wants_symlinks());
261 DEFINE_MAIN_GENERATOR_FUNCTION(run
);