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