]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/binfmt/binfmt.c
core: Record ExecMainStartTimestamp before forking
[thirdparty/systemd.git] / src / binfmt / binfmt.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <getopt.h>
5 #include <limits.h>
6 #include <stdbool.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11
12 #include "alloc-util.h"
13 #include "binfmt-util.h"
14 #include "build.h"
15 #include "conf-files.h"
16 #include "constants.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "log.h"
20 #include "main-func.h"
21 #include "pager.h"
22 #include "path-util.h"
23 #include "pretty-print.h"
24 #include "string-util.h"
25 #include "strv.h"
26
27 static CatFlags arg_cat_flags = CAT_CONFIG_OFF;
28 static PagerFlags arg_pager_flags = 0;
29 static bool arg_unregister = false;
30
31 static int delete_rule(const char *rulename) {
32 const char *fn = strjoina("/proc/sys/fs/binfmt_misc/", rulename);
33 return write_string_file(fn, "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
34 }
35
36 static int apply_rule(const char *filename, unsigned line, const char *rule) {
37 assert(filename);
38 assert(line > 0);
39 assert(rule);
40 assert(rule[0]);
41
42 _cleanup_free_ char *rulename = NULL;
43 int r;
44
45 rulename = strdupcspn(rule + 1, CHAR_TO_STR(rule[0]));
46 if (!rulename)
47 return log_oom();
48
49 if (!filename_is_valid(rulename) ||
50 STR_IN_SET(rulename, "register", "status"))
51 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
52 "%s:%u: Rule name '%s' is not valid, refusing.",
53 filename, line, rulename);
54 r = delete_rule(rulename);
55 if (r < 0 && r != -ENOENT)
56 log_warning_errno(r, "%s:%u: Failed to delete rule '%s', ignoring: %m",
57 filename, line, rulename);
58 if (r >= 0)
59 log_debug("%s:%u: Rule '%s' deleted.", filename, line, rulename);
60
61 r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, WRITE_STRING_FILE_DISABLE_BUFFER);
62 if (r < 0)
63 return log_error_errno(r, "%s:%u: Failed to add binary format '%s': %m",
64 filename, line, rulename);
65
66 log_debug("%s:%u: Binary format '%s' registered.", filename, line, rulename);
67 return 0;
68 }
69
70 static int apply_file(const char *filename, bool ignore_enoent) {
71 _cleanup_fclose_ FILE *f = NULL;
72 _cleanup_free_ char *pp = NULL;
73 int r;
74
75 assert(filename);
76
77 r = search_and_fopen(filename, "re", NULL, (const char**) CONF_PATHS_STRV("binfmt.d"), &f, &pp);
78 if (r < 0) {
79 if (ignore_enoent && r == -ENOENT)
80 return 0;
81
82 return log_error_errno(r, "Failed to open file '%s': %m", filename);
83 }
84
85 log_debug("Applying %s%s", pp, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
86 for (unsigned line = 1;; line++) {
87 _cleanup_free_ char *text = NULL;
88 int k;
89
90 k = read_stripped_line(f, LONG_LINE_MAX, &text);
91 if (k < 0)
92 return log_error_errno(k, "Failed to read file '%s': %m", pp);
93 if (k == 0)
94 break;
95
96 if (isempty(text))
97 continue;
98 if (strchr(COMMENTS, text[0]))
99 continue;
100
101 RET_GATHER(r, apply_rule(filename, line, text));
102 }
103
104 return r;
105 }
106
107 static int cat_config(char **files) {
108 pager_open(arg_pager_flags);
109
110 return cat_files(NULL, files, arg_cat_flags);
111 }
112
113 static int help(void) {
114 _cleanup_free_ char *link = NULL;
115 int r;
116
117 r = terminal_urlify_man("systemd-binfmt.service", "8", &link);
118 if (r < 0)
119 return log_oom();
120
121 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
122 "Registers binary formats with the kernel.\n\n"
123 " -h --help Show this help\n"
124 " --version Show package version\n"
125 " --cat-config Show configuration files\n"
126 " --tldr Show non-comment parts of configuration\n"
127 " --no-pager Do not pipe output into a pager\n"
128 " --unregister Unregister all existing entries\n"
129 "\nSee the %s for details.\n",
130 program_invocation_short_name,
131 link);
132
133 return 0;
134 }
135
136 static int parse_argv(int argc, char *argv[]) {
137 enum {
138 ARG_VERSION = 0x100,
139 ARG_CAT_CONFIG,
140 ARG_TLDR,
141 ARG_NO_PAGER,
142 ARG_UNREGISTER,
143 };
144
145 static const struct option options[] = {
146 { "help", no_argument, NULL, 'h' },
147 { "version", no_argument, NULL, ARG_VERSION },
148 { "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
149 { "tldr", no_argument, NULL, ARG_TLDR },
150 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
151 { "unregister", no_argument, NULL, ARG_UNREGISTER },
152 {}
153 };
154
155 int c;
156
157 assert(argc >= 0);
158 assert(argv);
159
160 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
161
162 switch (c) {
163
164 case 'h':
165 return help();
166
167 case ARG_VERSION:
168 return version();
169
170 case ARG_CAT_CONFIG:
171 arg_cat_flags = CAT_CONFIG_ON;
172 break;
173
174 case ARG_TLDR:
175 arg_cat_flags = CAT_TLDR;
176 break;
177
178 case ARG_NO_PAGER:
179 arg_pager_flags |= PAGER_DISABLE;
180 break;
181
182 case ARG_UNREGISTER:
183 arg_unregister = true;
184 break;
185
186 case '?':
187 return -EINVAL;
188
189 default:
190 assert_not_reached();
191 }
192
193 if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && argc > optind)
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
195 "Positional arguments are not allowed with --cat-config/--tldr or --unregister.");
196
197 return 1;
198 }
199
200 static int binfmt_mounted_warn(void) {
201 int r;
202
203 r = binfmt_mounted();
204 if (r < 0)
205 return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
206 if (r == 0)
207 log_debug("/proc/sys/fs/binfmt_misc is not mounted in read-write mode, skipping.");
208
209 return r;
210 }
211
212 static int run(int argc, char *argv[]) {
213 int r;
214
215 r = parse_argv(argc, argv);
216 if (r <= 0)
217 return r;
218
219 log_setup();
220
221 umask(0022);
222
223 r = 0;
224
225 if (arg_unregister)
226 return disable_binfmt();
227
228 if (argc > optind) {
229 r = binfmt_mounted_warn();
230 if (r <= 0)
231 return r;
232
233 for (int i = optind; i < argc; i++)
234 RET_GATHER(r, apply_file(argv[i], false));
235
236 } else {
237 _cleanup_strv_free_ char **files = NULL;
238
239 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
240 if (r < 0)
241 return log_error_errno(r, "Failed to enumerate binfmt.d files: %m");
242
243 if (arg_cat_flags != CAT_CONFIG_OFF)
244 return cat_config(files);
245
246 r = binfmt_mounted_warn();
247 if (r <= 0)
248 return r;
249
250 /* Flush out all rules */
251 r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
252 if (r < 0)
253 log_warning_errno(r, "Failed to flush binfmt_misc rules, ignoring: %m");
254 else
255 log_debug("Flushed all binfmt_misc rules.");
256
257 STRV_FOREACH(f, files)
258 RET_GATHER(r, apply_file(*f, true));
259 }
260
261 return r;
262 }
263
264 DEFINE_MAIN_FUNCTION(run);