140 lines
4.5 KiB
C
140 lines
4.5 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "escape.h"
|
|
#include "fd-util.h"
|
|
#include "fileio.h"
|
|
#include "generator.h"
|
|
#include "mkdir.h"
|
|
#include "proc-cmdline.h"
|
|
#include "special.h"
|
|
#include "specifier.h"
|
|
#include "strv.h"
|
|
|
|
static const char *arg_dest = NULL;
|
|
static char **arg_commands = NULL;
|
|
static char *arg_success_action = NULL;
|
|
static char *arg_failure_action = NULL;
|
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_commands, strv_freep);
|
|
STATIC_DESTRUCTOR_REGISTER(arg_success_action, freep);
|
|
STATIC_DESTRUCTOR_REGISTER(arg_failure_action, freep);
|
|
|
|
static int parse(const char *key, const char *value, void *data) {
|
|
int r;
|
|
|
|
if (proc_cmdline_key_streq(key, "systemd.run")) {
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
r = strv_extend(&arg_commands, value);
|
|
if (r < 0)
|
|
return log_oom();
|
|
|
|
} else if (proc_cmdline_key_streq(key, "systemd.run_success_action")) {
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
if (free_and_strdup(&arg_success_action, value) < 0)
|
|
return log_oom();
|
|
|
|
} else if (proc_cmdline_key_streq(key, "systemd.run_failure_action")) {
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
if (free_and_strdup(&arg_failure_action, value) < 0)
|
|
return log_oom();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate(void) {
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
const char *p;
|
|
char **c;
|
|
int r;
|
|
|
|
if (strv_isempty(arg_commands) && !arg_success_action)
|
|
return 0;
|
|
|
|
p = strjoina(arg_dest, "/kernel-command-line.service");
|
|
f = fopen(p, "wxe");
|
|
if (!f)
|
|
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
|
|
|
|
fputs("# Automatically generated by systemd-run-generator\n\n"
|
|
"[Unit]\n"
|
|
"Description=Command from Kernel Command Line\n"
|
|
"Documentation=man:systemd-run-generator(8)\n"
|
|
"SourcePath=/proc/cmdline\n", f);
|
|
|
|
if (!streq_ptr(arg_success_action, "none"))
|
|
fprintf(f, "SuccessAction=%s\n",
|
|
arg_success_action ?: "exit");
|
|
|
|
if (!streq_ptr(arg_failure_action, "none"))
|
|
fprintf(f, "FailureAction=%s\n",
|
|
arg_failure_action ?: "exit");
|
|
|
|
fputs("\n"
|
|
"[Service]\n"
|
|
"Type=oneshot\n"
|
|
"StandardOutput=journal+console\n", f);
|
|
|
|
STRV_FOREACH(c, arg_commands) {
|
|
_cleanup_free_ char *a = NULL;
|
|
|
|
a = specifier_escape(*c);
|
|
if (!a)
|
|
return log_oom();
|
|
|
|
fprintf(f, "ExecStart=%s\n", a);
|
|
}
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to write unit file %s: %m", p);
|
|
|
|
/* Let's create a a target we can link "default.target" to */
|
|
p = strjoina(arg_dest, "/kernel-command-line.target");
|
|
r = write_string_file(
|
|
p,
|
|
"# Automatically generated by systemd-run-generator\n\n"
|
|
"[Unit]\n"
|
|
"Description=Command from Kernel Command Line\n"
|
|
"Documentation=man:systemd-run-generator(8)\n"
|
|
"SourcePath=/proc/cmdline\n"
|
|
"Requires=kernel-command-line.service\n"
|
|
"After=kernel-command-line.service\n",
|
|
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to create unit file %s: %m", p);
|
|
|
|
/* And now redirect default.target to our new target */
|
|
p = strjoina(arg_dest, "/" SPECIAL_DEFAULT_TARGET);
|
|
if (symlink("kernel-command-line.target", p) < 0)
|
|
return log_error_errno(errno, "Failed to link unit file kernel-command-line.target → %s: %m", p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
|
int r;
|
|
|
|
assert_se(arg_dest = dest);
|
|
|
|
r = proc_cmdline_parse(parse, NULL, PROC_CMDLINE_RD_STRICT|PROC_CMDLINE_STRIP_RD_PREFIX);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
|
|
|
return generate();
|
|
}
|
|
|
|
DEFINE_MAIN_GENERATOR_FUNCTION(run);
|