From 35a1ff4cfe82810e7f52db07b67793af8c54c9f6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 Nov 2018 22:09:29 +0100 Subject: [PATCH] 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. --- meson.build | 8 ++ src/run-generator/run-generator.c | 147 ++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/run-generator/run-generator.c diff --git a/meson.build b/meson.build index 8b51d032b8..2c8935c833 100644 --- a/meson.build +++ b/meson.build @@ -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', diff --git a/src/run-generator/run-generator.c b/src/run-generator/run-generator.c new file mode 100644 index 0000000000..243a426cda --- /dev/null +++ b/src/run-generator/run-generator.c @@ -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);