add new run-generator

This is really useful for running commands like this:

        # systemd-run -i someimage.raw -b systemd.run='"some command line"'

This will now run the command line inside a small Type=oneshot service
and even propagate the exit code of the command back to the parent. And
all that with the full system booted up.

By default this causes the system to shutdown right after the command
completed, but this can be tweaked with systemd.run_success_action= and
systemd.run_failure_action=.

Note that when used in VMs the exit status can of course not be
propagate, as VMs don't really know a concept for that.
This commit is contained in:
Lennart Poettering 2018-11-15 22:09:29 +01:00
parent 7af67e9a8b
commit 35a1ff4cfe
2 changed files with 155 additions and 0 deletions

View File

@ -1631,6 +1631,14 @@ executable('systemd-debug-generator',
install : true,
install_dir : systemgeneratordir)
executable('systemd-run-generator',
'src/run-generator/run-generator.c',
include_directories : includes,
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true,
install_dir : systemgeneratordir)
executable('systemd-fstab-generator',
'src/fstab-generator/fstab-generator.c',
'src/core/mount-setup.c',

View File

@ -0,0 +1,147 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "generator.h"
#include "main-func.h"
#include "mkdir.h"
#include "proc-cmdline.h"
#include "specifier.h"
#include "strv.h"
static const char *arg_dest = "/tmp";
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, "/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(int argc, char *argv[]) {
int r;
log_setup_generator();
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return -EINVAL;
}
if (argc > 1)
arg_dest = argv[1];
umask(0022);
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_FUNCTION(run);