build-initvm: new files for static binary to register qemu binfmt_misc
authorJames Perkins <james.perkins@linuxfoundation.org>
Fri, 17 Sep 2010 23:18:19 +0000 (16:18 -0700)
committerJan-Simon Möller <jsmoeller@linuxfoundation.org>
Sun, 20 Mar 2011 19:18:03 +0000 (20:18 +0100)
Introduces initvm.c, which will be compiled by later to a binary,
$BUILD_DIR/initvm.  initvm will:

    1) mount proc and binfmt_misc and verify correct setup
    2) read qemu-reg from /.build or /usr/lib/build and for each line,
       install binfmt_misc support and verify success
    3) verify existence of /.build/build and run the build script

This functionality obviates the need for $BUILD_DIR/initscript_qemu_vm
and the bash-static and mount-static x86 packages, but does not remove
build's ability to use them.

Signed-off-by: James Perkins <james.perkins@linuxfoundation.org>
initvm.c [new file with mode: 0644]
qemu-reg [new file with mode: 0644]

diff --git a/initvm.c b/initvm.c
new file mode 100644 (file)
index 0000000..f878174
--- /dev/null
+++ b/initvm.c
@@ -0,0 +1,329 @@
+/*
+ * NAME
+ *     initvm - init for qemu, setup binfmt_misc launch build
+ *     
+ * SYNOPSIS
+ *     initvm
+ *
+ * DESCRIPTION
+ *     This is the kernel init script for virtual machines which will
+ *     be running executables for an embedded (non-native)
+ *     architecture. It registers binfmt_misc handlers for qemu and
+ *     executes the build script, and tests many assumptions.
+ *
+ * FILES
+ *     /.build/qemu-reg
+ *             text file with lines to stuff into the binfmt_misc
+ *             filesystem registration file
+ *     /.build/build
+ *             build script to execute once binfmts are set up
+ *
+ * AUTHOR
+ *     James Perkins <james.perkins@linuxfoundation.org>
+ */
+
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* to enable debugging, compile with -DDEBUG */
+#ifdef DEBUG
+#define DBG(x)                 do { x; } while(0)
+#else
+#define DBG(x)
+#endif
+
+/* function return codes */
+enum okfail { FAIL=0, OK=1 };
+
+/* qemu registration fields, see kernel/Documentation/binfmt_misc.txt */
+enum fields { ignore=0, name, type, offset, magic, mask, interpreter, flags };
+const char * const fieldnames[] = {
+       "ignore", "name", "type", "offset",
+       "magic", "mask", "interpreter", "flags"
+};
+const int n_fields = 8;
+
+/* files in useful places */
+#define SYSFS_BINFMT_MISC      "/proc/sys/fs/binfmt_misc"
+#define SYSFS_BINFMT_MISC_REG  "/proc/sys/fs/binfmt_misc/register"
+#define SYSFS_BINFMT_MISC_STAT "/proc/sys/fs/binfmt_misc/status"
+
+/* /usr/lib/build/x paths are copied to /.build inside a virtual machine */
+#define BINFMT_REGF_0          "/.build/qemu-reg"
+#define BINFMT_REGF_1          "/usr/lib/build/qemu-reg"
+#define BUILD                  "/.build/build"
+
+/* useful constant arrays */
+static char *rx_files[] = { "/proc", "/proc/sys", "/proc/sys/fs",
+        SYSFS_BINFMT_MISC, NULL };
+static char *w_files[] = { SYSFS_BINFMT_MISC_REG, NULL };
+
+static char* const args[] = { BUILD, NULL };
+
+/* test access modes for files, return OK or FAIL */
+enum okfail test_access_files(char *files[], int mode, const char *errstr)
+{
+       int i;
+
+       for (i = 0; files[i] != NULL; i++) {
+               if (access(files[i], mode) != 0) {
+                       fprintf(stderr, "%s: %s: fails test\n",
+                               files[i], errstr);
+                       return FAIL;
+               }
+       }
+
+       return OK;
+}
+
+/* find a string in the given file, return OK or FAIL */
+enum okfail strfile(const char *filename, const char *string)
+{
+       char buf[BUFSIZ];
+       FILE *fp;
+       enum okfail found = FAIL;
+
+       fp = fopen(filename, "r");
+       if (fp == NULL)
+       {
+               perror(filename);
+               return FAIL;
+       }
+       while (fgets(buf, sizeof(buf), fp) != NULL)
+       {
+               if (strcmp(buf, string) == 0) {
+                       found = OK;
+                       break;
+               }
+
+       }
+       (void)fclose(fp);
+
+       return found;
+}
+
+/* write the file with given string, return OK or FAIL */
+enum okfail write_file_string(const char *filename, const char *string)
+{
+       int fd;
+
+       if ((fd = open(filename, O_WRONLY)) == -1)
+       {
+               perror(filename);
+               return FAIL;
+       }
+
+       if (write(fd, string, strlen(string)) == -1)
+       {
+               perror("write");
+               fprintf(stderr, "%s: write failed\n", filename);
+               close(fd);
+               return FAIL;
+       }
+
+       close(fd);
+       return OK;
+}
+
+#ifdef DEBUG
+/* dump contents of the file to stderr, return OK or FAIL */
+enum okfail dump_file(char *path)
+{
+       FILE *fp;
+       char buf[BUFSIZ];
+
+       fp = fopen(path, "r");
+       if (fp == NULL) {
+               perror(path);
+               return FAIL;
+       }
+
+       while (fgets(buf, sizeof(buf), fp) != NULL)
+       {
+               fputs(buf, stderr);
+       }
+
+       fclose(fp);
+       return OK;
+}
+#endif /* DEBUG */
+
+/* parse datafile and register (to regfile) all binary formats found */
+enum okfail binfmt_register(char *datafile, char *regfile)
+{
+       char buf[BUFSIZ];
+       FILE *fp;
+       int line;
+
+       fp = fopen(datafile, "r");
+       if (fp == NULL)
+       {
+               perror(datafile);
+               return FAIL;
+       }
+
+       for (line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++)
+       {
+               char tokens[BUFSIZ];
+               char *s = tokens;
+               char *f[n_fields];      /* field content pointers */
+               int n;                  /* current field */
+               char path[BUFSIZ];
+
+               if (buf[0] != ':')      /* non-data input line */
+               {
+                       goto skip;
+               }
+
+               /* copy buf and tokenize :-seperated fields into f[] */
+               strcpy(tokens, buf);
+               for (n = 0; s != NULL && n < n_fields; n++)
+               {
+                       f[n] = strsep(&s, ":");
+               }
+
+#ifdef DEBUG
+               int i;
+               fprintf(stderr, "DEBUG: line %d, fields %d:\n",  line, n);
+               for (i = name; i < n; i++)
+               {
+                       fprintf(stderr, " %s %s\n", fieldnames[i], f[i]);
+               }
+#endif /* DEBUG */
+
+               if (n == n_fields && s != NULL)
+               {
+                       fprintf(stderr, "%s: line %d: extra fields, ignoring."
+                               " Content: %s", datafile, line, buf);
+                       goto skip;
+               }
+
+               if (n < n_fields)
+               {
+                       fprintf(stderr, "%s: line %d: missing fields, ignoring."
+                               " Content: %s", datafile, line, buf);
+                       goto skip;
+               }
+
+
+               if (access(f[interpreter], X_OK) != 0) {
+                       fprintf(stderr, 
+                               "%s: line %d: interpreter '%s' not found,"
+                               " ignoring\n", datafile, line, f[interpreter]);
+                       goto skip;
+               }
+
+               if (!write_file_string(regfile, buf)) {
+                       fprintf(stderr, "%s: line %d: write failed."
+                               " Content: %s\n", datafile, line, buf);
+                       (void)fclose(fp);
+                       return FAIL;
+               }
+
+               /* verify registration completed correctly */
+               snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
+
+               if (access(path, R_OK) != 0) {
+                       fprintf(stderr, 
+                               "%s: line %d: binfmt path not created, content '%s'\n",
+                               path, line, buf);
+                       (void)fclose(fp);
+                       return FAIL;
+               }
+
+               DBG(fprintf(stderr, "dumping: %s\n", path));
+               DBG(dump_file(path));
+
+skip:
+               ;
+       }
+
+
+       (void)fclose(fp);
+
+       return OK;
+}
+
+/* set up/verify binfmt FS support, program more binfmts, and launch build */
+int main(int argc, char* argv[], char* env[])
+{
+       int retval;
+
+       /* mount proc filesystem if it isn't already */
+       if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
+               if (errno != EBUSY) {
+                       perror("mount: /proc");
+                       exit(1);
+               }
+       }
+
+       /* try to load binfmt module if present, no big deal if it fails */
+       if ((retval = system("/sbin/modprobe binfmt_misc")) != 0) {
+               DBG(fprintf(stderr, "modprobe binfmt_misc exit code %d\n",
+                       retval));
+       }
+
+       /* mount binfmt filesystem */
+       if (mount("binfmt_misc", SYSFS_BINFMT_MISC, "binfmt_misc", MS_MGC_VAL,
+               NULL) == -1) {
+               if (errno != EBUSY) {
+                       perror("mount: binfmt_misc, " SYSFS_BINFMT_MISC);
+               }
+       }
+
+       /* verify all paths resulting from this are OK */
+       if (!test_access_files(rx_files, R_OK|X_OK, "read/search")) {
+               exit(1);
+       }
+       if (!test_access_files(w_files, W_OK, "write")) {
+               exit(1);
+       }
+
+       if (!strfile("/proc/filesystems", "nodev\tbinfmt_misc\n")) {
+               fprintf(stderr,
+                       "/proc/filesystems: binfmt_misc support missing\n");
+               exit(1);
+       }
+
+       if (!strfile(SYSFS_BINFMT_MISC_STAT, "enabled\n")) {
+               fprintf(stderr,
+                       "%s: binfmt_misc filesystem support not enabled\n",
+                       SYSFS_BINFMT_MISC_STAT);
+               exit(1);
+       }
+
+       /* setup all done, do the registration */
+       if (!binfmt_register(BINFMT_REGF_0, SYSFS_BINFMT_MISC_REG)) {
+               fprintf(stderr, "%s: failed. Trying alternate binfmt file\n",
+                       BINFMT_REGF_0);
+               if (!binfmt_register(BINFMT_REGF_1, SYSFS_BINFMT_MISC_REG)) {
+                       fprintf(stderr, "%s: binfmt registration failed\n",
+                               BINFMT_REGF_1);
+                       exit(1);
+               }
+       }
+
+       /* if we are the init process, start build */
+       if (getpid() == 1)
+       {
+               if (access(BUILD, F_OK) != 0) {
+                       fprintf(stderr, "%s: build executable missing\n",
+                               BUILD);
+                       exit(1);
+               }
+               if (access(BUILD, X_OK) != 0) {
+                       fprintf(stderr, "%s: not executable\n", BUILD);
+                       exit(1);
+               }
+               execve(BUILD, args, env);
+               perror("execve");
+               exit(1);
+       }
+
+       /* success! */
+       exit(0);
+}
diff --git a/qemu-reg b/qemu-reg
new file mode 100644 (file)
index 0000000..9e040c8
--- /dev/null
+++ b/qemu-reg
@@ -0,0 +1,13 @@
+# register qemu binfmts
+#  - used by initvm, build common_functions and init_buildsystem
+# derived from /usr/sbin/qemu-binfmt-conf.sh
+
+:arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm:
+:armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-armeb:
+:ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-ppc:
+:mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-mips:
+:mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-mipsel:
+:mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-mipsn32:
+:mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-mipsn32el:
+:mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-mips64:
+:mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-mips64el: