1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <unistd.h>
4 
5 #include "alloc-util.h"
6 #include "escape.h"
7 #include "fd-util.h"
8 #include "fileio.h"
9 #include "generator.h"
10 #include "mkdir.h"
11 #include "proc-cmdline.h"
12 #include "special.h"
13 #include "specifier.h"
14 #include "strv.h"
15 
16 static const char *arg_dest = NULL;
17 static char **arg_commands = NULL;
18 static char *arg_success_action = NULL;
19 static char *arg_failure_action = NULL;
20 
21 STATIC_DESTRUCTOR_REGISTER(arg_commands, strv_freep);
22 STATIC_DESTRUCTOR_REGISTER(arg_success_action, freep);
23 STATIC_DESTRUCTOR_REGISTER(arg_failure_action, freep);
24 
parse(const char * key,const char * value,void * data)25 static int parse(const char *key, const char *value, void *data) {
26         int r;
27 
28         if (proc_cmdline_key_streq(key, "systemd.run")) {
29 
30                 if (proc_cmdline_value_missing(key, value))
31                         return 0;
32 
33                 r = strv_extend(&arg_commands, value);
34                 if (r < 0)
35                         return log_oom();
36 
37         } else if (proc_cmdline_key_streq(key, "systemd.run_success_action")) {
38 
39                 if (proc_cmdline_value_missing(key, value))
40                         return 0;
41 
42                 return free_and_strdup_warn(&arg_success_action, value);
43 
44         } else if (proc_cmdline_key_streq(key, "systemd.run_failure_action")) {
45 
46                 if (proc_cmdline_value_missing(key, value))
47                         return 0;
48 
49                 return free_and_strdup_warn(&arg_failure_action, value);
50         }
51 
52         return 0;
53 }
54 
generate(void)55 static int generate(void) {
56         _cleanup_fclose_ FILE *f = NULL;
57         const char *p;
58         int r;
59 
60         if (strv_isempty(arg_commands) && !arg_success_action)
61                 return 0;
62 
63         p = strjoina(arg_dest, "/kernel-command-line.service");
64         f = fopen(p, "wxe");
65         if (!f)
66                 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
67 
68         fputs("# Automatically generated by systemd-run-generator\n\n"
69               "[Unit]\n"
70               "Description=Command from Kernel Command Line\n"
71               "Documentation=man:systemd-run-generator(8)\n"
72               "SourcePath=/proc/cmdline\n", f);
73 
74         if (!streq_ptr(arg_success_action, "none"))
75                 fprintf(f, "SuccessAction=%s\n",
76                         arg_success_action ?: "exit");
77 
78         if (!streq_ptr(arg_failure_action, "none"))
79                 fprintf(f, "FailureAction=%s\n",
80                         arg_failure_action ?: "exit");
81 
82         fputs("\n"
83               "[Service]\n"
84               "Type=oneshot\n"
85               "StandardOutput=journal+console\n", f);
86 
87         STRV_FOREACH(c, arg_commands) {
88                 _cleanup_free_ char *a = NULL;
89 
90                 a = specifier_escape(*c);
91                 if (!a)
92                         return log_oom();
93 
94                 fprintf(f, "ExecStart=%s\n", a);
95         }
96 
97         r = fflush_and_check(f);
98         if (r < 0)
99                 return log_error_errno(r, "Failed to write unit file %s: %m", p);
100 
101         /* Let's create a target we can link "default.target" to */
102         p = strjoina(arg_dest, "/kernel-command-line.target");
103         r = write_string_file(
104                         p,
105                         "# Automatically generated by systemd-run-generator\n\n"
106                         "[Unit]\n"
107                         "Description=Command from Kernel Command Line\n"
108                         "Documentation=man:systemd-run-generator(8)\n"
109                         "SourcePath=/proc/cmdline\n"
110                         "Requires=kernel-command-line.service\n"
111                         "After=kernel-command-line.service\n",
112                         WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW);
113         if (r < 0)
114                 return log_error_errno(r, "Failed to create unit file %s: %m", p);
115 
116         /* And now redirect default.target to our new target */
117         p = strjoina(arg_dest, "/" SPECIAL_DEFAULT_TARGET);
118         if (symlink("kernel-command-line.target", p) < 0)
119                 return log_error_errno(errno, "Failed to link unit file kernel-command-line.target → %s: %m", p);
120 
121         return 0;
122 }
123 
run(const char * dest,const char * dest_early,const char * dest_late)124 static int run(const char *dest, const char *dest_early, const char *dest_late) {
125         int r;
126 
127         assert_se(arg_dest = dest);
128 
129         r = proc_cmdline_parse(parse, NULL, PROC_CMDLINE_RD_STRICT|PROC_CMDLINE_STRIP_RD_PREFIX);
130         if (r < 0)
131                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
132 
133         return generate();
134 }
135 
136 DEFINE_MAIN_GENERATOR_FUNCTION(run);
137