Skip to content

Commit

Permalink
build-initvm: new files for static binary to register qemu binfmt_misc
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
James Perkins authored and Jan-Simon Möller committed Mar 20, 2011
1 parent 0215004 commit aecabd9
Show file tree
Hide file tree
Showing 2 changed files with 342 additions and 0 deletions.
329 changes: 329 additions & 0 deletions 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);
}
13 changes: 13 additions & 0 deletions 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:

0 comments on commit aecabd9

Please sign in to comment.