summaryrefslogtreecommitdiffstats
path: root/src/run-generator
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-11-15 22:09:29 +0100
committerLennart Poettering <lennart@poettering.net>2018-11-27 09:44:40 +0100
commit35a1ff4cfe82810e7f52db07b67793af8c54c9f6 (patch)
tree35cbc5c1280160c509c794510ff4bb3c005b0804 /src/run-generator
parentcore: allow to set exit status when using SuccessAction=/FailureAction=exit i... (diff)
downloadsystemd-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')
-rw-r--r--src/run-generator/run-generator.c147
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);