3 * initvm - init for qemu, setup binfmt_misc launch build
9 * This is the kernel init script for virtual machines which will
10 * be running executables for an embedded (non-native)
11 * architecture. It registers binfmt_misc handlers for qemu and
12 * executes the build script, and tests many assumptions.
16 * text file with lines to stuff into the binfmt_misc
17 * filesystem registration file
19 * build script to execute once binfmts are set up
22 * Copyright (c) 2012 James Perkins <james.perkins@linuxfoundation.org>
23 * Adrian Schroeter <adrian@suse.de>
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License version 2 or 3 as
27 * published by the Free Software Foundation.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program (see the file COPYING); if not, write to the
36 * Free Software Foundation, Inc.,
37 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
40 #include <sys/mount.h>
42 #include <sys/utsname.h>
50 /* to enable debugging, compile with -DDEBUG */
52 #define DBG(x) do { x; } while(0)
57 /* function return codes */
58 enum okfail { FAIL=0, OK=1 };
60 /* qemu registration fields, see kernel/Documentation/binfmt_misc.txt */
61 enum fields { ignore=0, name, type, offset, magic, mask, interpreter, flags };
62 const char * const fieldnames[] = {
63 "ignore", "name", "type", "offset",
64 "magic", "mask", "interpreter", "flags"
66 const int n_fields = 8;
68 /* files in useful places */
69 #define SYSFS_BINFMT_MISC "/proc/sys/fs/binfmt_misc"
70 #define SYSFS_BINFMT_MISC_REG "/proc/sys/fs/binfmt_misc/register"
71 #define SYSFS_BINFMT_MISC_STAT "/proc/sys/fs/binfmt_misc/status"
73 /* /usr/lib/build/x paths are copied to /.build inside a virtual machine */
74 #define BINFMT_REGF_0 "/.build/qemu-reg"
75 #define BINFMT_REGF_1 "/usr/lib/build/qemu-reg"
76 #define BUILD "/.build/build"
78 /* useful constant arrays */
79 static char *rx_files[] = { "/proc", "/proc/sys", "/proc/sys/fs",
80 SYSFS_BINFMT_MISC, NULL };
81 static char *w_files[] = { SYSFS_BINFMT_MISC_REG, NULL };
83 static char* const args[] = { BUILD, NULL };
85 /* test access modes for files, return OK or FAIL */
86 enum okfail test_access_files(char *files[], int mode, const char *errstr)
90 for (i = 0; files[i] != NULL; i++) {
91 if (access(files[i], mode) != 0) {
92 fprintf(stderr, "%s: %s: fails test\n",
101 /* find a string in the given file, return OK or FAIL */
102 enum okfail strfile(const char *filename, const char *string)
106 enum okfail found = FAIL;
108 fp = fopen(filename, "r");
114 while (fgets(buf, sizeof(buf), fp) != NULL)
116 if (strcmp(buf, string) == 0) {
127 /* write the file with given string, return OK or FAIL */
128 enum okfail write_file_string(const char *filename, const char *string)
132 if ((fd = open(filename, O_WRONLY)) == -1)
138 if (write(fd, string, strlen(string)) == -1)
141 fprintf(stderr, "%s: write failed\n", filename);
151 /* dump contents of the file to stderr, return OK or FAIL */
152 enum okfail dump_file(char *path)
157 fp = fopen(path, "r");
163 while (fgets(buf, sizeof(buf), fp) != NULL)
173 /* parse datafile and register (to regfile) all binary formats found */
174 enum okfail binfmt_register(char *datafile, char *regfile)
179 struct utsname myuname;
182 fp = fopen(datafile, "r");
189 for (line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++)
194 char *f[n_fields]; /* field content pointers */
195 int n; /* current field */
198 if (buf[0] != ':') /* non-data input line */
202 blacklist = strchr(buf, ' ');
210 eol = strchr(blacklist, '\n');
214 for (n = 0; blacklist != NULL; n++)
216 char *bp = strsep(&blacklist, " ");
217 if (!strcmp(bp, myuname.machine)) {
219 fprintf(stderr, " skipping on hostarch %s line %s\n", bp, buf);
229 /* copy buf and tokenize :-seperated fields into f[] */
231 for (n = 0; s != NULL && n < n_fields; n++)
233 f[n] = strsep(&s, ":");
238 fprintf(stderr, "DEBUG: line %d, fields %d:\n", line, n);
239 for (i = name; i < n; i++)
241 fprintf(stderr, " %s %s\n", fieldnames[i], f[i]);
245 if (n == n_fields && s != NULL)
247 fprintf(stderr, "%s: line %d: extra fields, ignoring."
248 " Content: %s", datafile, line, buf);
254 fprintf(stderr, "%s: line %d: missing fields, ignoring."
255 " Content: %s", datafile, line, buf);
260 /* Is an interpreter for this arch already registered? */
261 snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
262 ret=access(path, F_OK);
266 "interpreter for '%s' already registered, ignoring\n",
273 "registering interpreter for '%s'...\n",
277 /* Does the interpreter exists? */
278 ret=access(f[interpreter], X_OK);
281 "%s: line %d: interpreter '%s' not found,"
282 " ignoring, return %d\n", datafile, line, f[interpreter], ret);
286 if (!write_file_string(regfile, buf)) {
287 fprintf(stderr, "%s: line %d: write failed."
288 " Content: %s\n", datafile, line, buf);
293 /* verify registration completed correctly */
294 snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
296 if (access(path, R_OK) != 0) {
298 "%s: line %d: binfmt path not created, content '%s'\n",
304 DBG(fprintf(stderr, "dumping: %s\n", path));
305 DBG(dump_file(path));
314 /* set up/verify binfmt FS support, program more binfmts, and launch build */
315 int main(int argc, char* argv[], char* env[])
318 char buf[BUFSIZ], *build_dir;
320 if (system("mount | grep 'proc on /proc type proc' >/dev/null") != 0) {
321 /* mount proc filesystem if it isn't already. */
322 if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
323 if (errno != EBUSY) {
324 perror("mount: /proc");
330 /* need to have binfmt_misc loaded already */
331 if (system("mount | grep 'binfmt_misc on /proc/sys/fs/binfmt_misc' >/dev/null") != 0) {
332 /* try to load binfmt module if present, no big deal if it fails */
333 if ((retval = system("/sbin/modprobe binfmt_misc")) != 0) {
334 DBG(fprintf(stderr, "modprobe binfmt_misc exit code %d\n",
338 /* mount binfmt filesystem */
339 if (mount("binfmt_misc", SYSFS_BINFMT_MISC, "binfmt_misc", MS_MGC_VAL,
341 if (errno != EBUSY) {
342 perror("mount: binfmt_misc, " SYSFS_BINFMT_MISC);
347 /* verify all paths resulting from this are OK */
348 if (!test_access_files(rx_files, R_OK|X_OK, "read/search")) {
351 if (!test_access_files(w_files, W_OK, "write")) {
355 if (!strfile("/proc/filesystems", "nodev\tbinfmt_misc\n")) {
357 "/proc/filesystems: binfmt_misc support missing\n");
361 if (!strfile(SYSFS_BINFMT_MISC_STAT, "enabled\n")) {
363 "%s: binfmt_misc filesystem support not enabled\n",
364 SYSFS_BINFMT_MISC_STAT);
369 build_dir = getenv("BUILD_DIR");
370 if (build_dir && strlen(build_dir) < sizeof(buf) - 10)
371 sprintf(buf, "%s/qemu-reg", build_dir);
373 if (!*buf || !binfmt_register(buf, SYSFS_BINFMT_MISC_REG)) {
374 /* setup all done, do the registration */
375 if (!binfmt_register(BINFMT_REGF_0, SYSFS_BINFMT_MISC_REG)) {
376 fprintf(stderr, "%s: failed. Trying alternate binfmt file\n",
378 if (!binfmt_register(BINFMT_REGF_1, SYSFS_BINFMT_MISC_REG)) {
379 fprintf(stderr, "%s: binfmt registration failed\n",
386 /* if we are the init process, start build */
389 if (access(BUILD, F_OK) != 0) {
390 fprintf(stderr, "%s: build executable missing\n",
394 if (access(BUILD, X_OK) != 0) {
395 fprintf(stderr, "%s: not executable\n", BUILD);
398 execve(BUILD, args, env);
399 perror("execve of "BUILD);