chiark / gitweb /
3487602e467d88de14356f055151562e551c35e8
[elogind.git] / src / binfmt / binfmt.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <getopt.h>
30
31 #include "log.h"
32 #include "hashmap.h"
33 #include "strv.h"
34 #include "util.h"
35 #include "conf-files.h"
36 #include "fileio.h"
37 #include "build.h"
38
39 static const char conf_file_dirs[] =
40         "/etc/binfmt.d\0"
41         "/run/binfmt.d\0"
42         "/usr/local/lib/binfmt.d\0"
43         "/usr/lib/binfmt.d\0"
44 #ifdef HAVE_SPLIT_USR
45         "/lib/binfmt.d\0"
46 #endif
47         ;
48
49 static int delete_rule(const char *rule) {
50         _cleanup_free_ char *x = NULL, *fn = NULL;
51         char *e;
52
53         assert(rule[0]);
54
55         x = strdup(rule);
56         if (!x)
57                 return log_oom();
58
59         e = strchrnul(x+1, x[0]);
60         *e = 0;
61
62         fn = strappend("/proc/sys/fs/binfmt_misc/", x+1);
63         if (!fn)
64                 return log_oom();
65
66         return write_string_file(fn, "-1");
67 }
68
69 static int apply_rule(const char *rule) {
70         int r;
71
72         delete_rule(rule);
73
74         r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule);
75         if (r < 0) {
76                 log_error("Failed to add binary format: %s", strerror(-r));
77                 return r;
78         }
79
80         return 0;
81 }
82
83 static int apply_file(const char *path, bool ignore_enoent) {
84         _cleanup_fclose_ FILE *f = NULL;
85         int r;
86
87         assert(path);
88
89         r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
90         if (r < 0) {
91                 if (ignore_enoent && r == -ENOENT)
92                         return 0;
93
94                 log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
95                 return r;
96         }
97
98         log_debug("apply: %s\n", path);
99         for (;;) {
100                 char l[LINE_MAX], *p;
101                 int k;
102
103                 if (!fgets(l, sizeof(l), f)) {
104                         if (feof(f))
105                                 break;
106
107                         log_error("Failed to read file '%s', ignoring: %m", path);
108                         return -errno;
109                 }
110
111                 p = strstrip(l);
112                 if (!*p)
113                         continue;
114                 if (strchr(COMMENTS "\n", *p))
115                         continue;
116
117                 k = apply_rule(p);
118                 if (k < 0 && r == 0)
119                         r = k;
120         }
121
122         return r;
123 }
124
125 static int help(void) {
126
127         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
128                "Registers binary formats.\n\n"
129                "  -h --help             Show this help\n"
130                "     --version          Show package version\n",
131                program_invocation_short_name);
132
133         return 0;
134 }
135
136 static int parse_argv(int argc, char *argv[]) {
137
138         enum {
139                 ARG_VERSION = 0x100,
140         };
141
142         static const struct option options[] = {
143                 { "help",      no_argument,       NULL, 'h'           },
144                 { "version",   no_argument,       NULL, ARG_VERSION   },
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                         puts(PACKAGE_STRING);
162                         puts(SYSTEMD_FEATURES);
163                         return 0;
164
165                 case '?':
166                         return -EINVAL;
167
168                 default:
169                         assert_not_reached("Unhandled option");
170                 }
171         }
172
173         return 1;
174 }
175
176 int main(int argc, char *argv[]) {
177         int r, k;
178
179         r = parse_argv(argc, argv);
180         if (r <= 0)
181                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
182
183         log_set_target(LOG_TARGET_AUTO);
184         log_parse_environment();
185         log_open();
186
187         umask(0022);
188
189         r = 0;
190
191         if (argc > optind) {
192                 int i;
193
194                 for (i = optind; i < argc; i++) {
195                         k = apply_file(argv[i], false);
196                         if (k < 0 && r == 0)
197                                 r = k;
198                 }
199         } else {
200                 _cleanup_strv_free_ char **files = NULL;
201                 char **f;
202
203                 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
204                 if (r < 0) {
205                         log_error("Failed to enumerate binfmt.d files: %s", strerror(-r));
206                         goto finish;
207                 }
208
209                 /* Flush out all rules */
210                 write_string_file("/proc/sys/fs/binfmt_misc/status", "-1");
211
212                 STRV_FOREACH(f, files) {
213                         k = apply_file(*f, true);
214                         if (k < 0 && r == 0)
215                                 r = k;
216                 }
217         }
218
219 finish:
220         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
221 }