diff options
author | Lennart Poettering <lennart@poettering.net> | 2018-11-15 22:09:29 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2018-11-27 09:44:40 +0100 |
commit | 35a1ff4cfe82810e7f52db07b67793af8c54c9f6 (patch) | |
tree | 35cbc5c1280160c509c794510ff4bb3c005b0804 /src/run-generator/run-generator.c | |
parent | core: allow to set exit status when using SuccessAction=/FailureAction=exit i... (diff) | |
download | systemd-35a1ff4cfe82810e7f52db07b67793af8c54c9f6.tar.xz systemd-35a1ff4cfe82810e7f52db07b67793af8c54c9f6.zip |
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.
Diffstat (limited to 'src/run-generator/run-generator.c')
-rw-r--r-- | src/run-generator/run-generator.c | 147 |
1 files changed, 147 insertions, 0 deletions
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); |