]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/binfmt/binfmt.c
fa3d6b0e26df90c9bad3ec0af08d69b826dc9358
[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 bool arg_cat_config = false;
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 char *p;
89 int k;
90
91 k = read_line(f, LONG_LINE_MAX, &text);
92 if (k < 0)
93 return log_error_errno(k, "Failed to read file '%s': %m", pp);
94 if (k == 0)
95 break;
96
97 p = strstrip(text);
98 if (isempty(p))
99 continue;
100 if (strchr(COMMENTS, p[0]))
101 continue;
102
103 RET_GATHER(r, apply_rule(filename, line, p));
104 }
105
106 return r;
107 }
108
109 static int help(void) {
110 _cleanup_free_ char *link = NULL;
111 int r;
112
113 r = terminal_urlify_man("systemd-binfmt.service", "8", &link);
114 if (r < 0)
115 return log_oom();
116
117 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
118 "Registers binary formats with the kernel.\n\n"
119 " -h --help Show this help\n"
120 " --version Show package version\n"
121 " --cat-config Show configuration files\n"
122 " --no-pager Do not pipe output into a pager\n"
123 " --unregister Unregister all existing entries\n"
124 "\nSee the %s for details.\n",
125 program_invocation_short_name,
126 link);
127
128 return 0;
129 }
130
131 static int parse_argv(int argc, char *argv[]) {
132 enum {
133 ARG_VERSION = 0x100,
134 ARG_CAT_CONFIG,
135 ARG_NO_PAGER,
136 ARG_UNREGISTER,
137 };
138
139 static const struct option options[] = {
140 { "help", no_argument, NULL, 'h' },
141 { "version", no_argument, NULL, ARG_VERSION },
142 { "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
143 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
144 { "unregister", no_argument, NULL, ARG_UNREGISTER },
145 {}
146 };
147
148 int c;
149
150 assert(argc >= 0);
151 assert(argv);
152
153 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
154
155 switch (c) {
156
157 case 'h':
158 return help();
159
160 case ARG_VERSION:
161 return version();
162
163 case ARG_CAT_CONFIG:
164 arg_cat_config = true;
165 break;
166
167 case ARG_NO_PAGER:
168 arg_pager_flags |= PAGER_DISABLE;
169 break;
170
171 case ARG_UNREGISTER:
172 arg_unregister = true;
173 break;
174
175 case '?':
176 return -EINVAL;
177
178 default:
179 assert_not_reached();
180 }
181
182 if ((arg_unregister || arg_cat_config) && argc > optind)
183 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
184 "Positional arguments are not allowed with --cat-config or --unregister");
185
186 return 1;
187 }
188
189 static int binfmt_mounted_warn(void) {
190 int r;
191
192 r = binfmt_mounted();
193 if (r < 0)
194 return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
195 if (r == 0)
196 log_debug("/proc/sys/fs/binfmt_misc is not mounted in read-write mode, skipping.");
197
198 return r;
199 }
200
201 static int run(int argc, char *argv[]) {
202 int r;
203
204 r = parse_argv(argc, argv);
205 if (r <= 0)
206 return r;
207
208 log_setup();
209
210 umask(0022);
211
212 r = 0;
213
214 if (arg_unregister)
215 return disable_binfmt();
216
217 if (argc > optind) {
218 r = binfmt_mounted_warn();
219 if (r <= 0)
220 return r;
221
222 for (int i = optind; i < argc; i++)
223 RET_GATHER(r, apply_file(argv[i], false));
224
225 } else {
226 _cleanup_strv_free_ char **files = NULL;
227
228 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
229 if (r < 0)
230 return log_error_errno(r, "Failed to enumerate binfmt.d files: %m");
231
232 if (arg_cat_config) {
233 pager_open(arg_pager_flags);
234
235 return cat_files(NULL, files, /* flags= */ 0);
236 }
237
238 r = binfmt_mounted_warn();
239 if (r <= 0)
240 return r;
241
242 /* Flush out all rules */
243 r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
244 if (r < 0)
245 log_warning_errno(r, "Failed to flush binfmt_misc rules, ignoring: %m");
246 else
247 log_debug("Flushed all binfmt_misc rules.");
248
249 STRV_FOREACH(f, files)
250 RET_GATHER(r, apply_file(*f, true));
251 }
252
253 return r;
254 }
255
256 DEFINE_MAIN_FUNCTION(run);