[lxc-devel] [PATCH] lxc-debian: allow to specify a binfmt interpreter

Laurent Vivier laurent at vivier.eu
Mon Jun 13 12:34:06 UTC 2016


If you specify an interpreter path with "-I" or "--interpreter-path",
the architecture of the debian container can differ from the one of
the host.

Before creating the container, binfmt must be configured on the host:
the script checks the name of the interpreter in /proc/sys/fs/binfmt_misc/
to know where to install it in the container.

To create a MIPS container on an x86_64 host:

$ cat /proc/sys/fs/binfmt_misc/qemu-mips
enabled
interpreter //qemu-mips
flags: OC
offset 0
magic 7f454c4601020100000000000000000000020008
mask ffffffffffffff00fffffffffffffffffffeffff

$ sudo lxc-create -n virtmips-stretch -t debian -- \
                 --arch=mips \
                 --interpreter-path=./mips-linux-user/qemu-mips \
                 --mirror=http://ftp.debian.org/debian \
                 --release=stretch

Signed-off-by: Laurent Vivier <laurent at vivier.eu>
---
 templates/lxc-debian.in | 115 ++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 91 insertions(+), 24 deletions(-)

diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in
index b5af844..56953b6 100644
--- a/templates/lxc-debian.in
+++ b/templates/lxc-debian.in
@@ -41,6 +41,29 @@ LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
 # Allows the lxc-cache directory to be set by environment variable
 LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"}
 
+find_interpreter()
+{
+    given_interpreter=$(basename "$1")
+
+    if [ ! -d /proc/sys/fs/binfmt_misc/ ] ; then
+        return 1
+    fi
+    for file in /proc/sys/fs/binfmt_misc/* ; do
+        if [ "$file" = "/proc/sys/fs/binfmt_misc/register" -o \
+             "$file" = "/proc/sys/fs/binfmt_misc/status" ] ; then
+            continue
+        fi
+        interpreter_path=$(sed -n "/^interpreter/s/interpreter \([^[:space:]]*\)/\1/p" "$file")
+        interpreter=$(basename $interpreter_path)
+
+        if [ "$given_interpreter" = "$interpreter" ] ; then
+            echo "$interpreter_path"
+            return 0
+        fi
+    done
+    return 1
+}
+
 configure_debian()
 {
     rootfs=$1
@@ -252,6 +275,8 @@ openssh-server
     cache=$1
     arch=$2
     release=$3
+    interpreter="$4"
+    interpreter_path="$5"
 
     trap cleanup EXIT SIGHUP SIGINT SIGTERM
 
@@ -285,12 +310,33 @@ openssh-server
 
     # download a mini debian into a cache
     echo "Downloading debian minimal ..."
-    debootstrap --verbose --variant=minbase --arch=$arch \
-        --include=$packages --keyring=${releasekeyring} \
-        "$release" "$cache/partial-$release-$arch" $MIRROR
-    if [ $? -ne 0 ]; then
-        echo "Failed to download the rootfs, aborting."
-        return 1
+    if [ "$interpreter" = "" ] ; then
+        debootstrap --verbose --variant=minbase --arch=$arch \
+            --include=$packages --keyring=${releasekeyring} \
+            "$release" "$cache/partial-$release-$arch" $MIRROR
+        if [ $? -ne 0 ]; then
+            echo "Failed to download the rootfs, aborting."
+            return 1
+        fi
+    else
+        debootstrap --foreign --verbose --variant=minbase --arch=$arch \
+            --include=$packages --keyring=${releasekeyring} \
+            "$release" "$cache/partial-$release-$arch" $MIRROR
+        if [ $? -ne 0 ]; then
+            echo "Failed to download the rootfs, aborting."
+            return 1
+        fi
+        mkdir -p $(basename "$cache/partial-$release-$arch/$interpreter_path")
+        cp "$interpreter" "$cache/partial-$release-$arch/$interpreter_path"
+        if [ $? -ne 0 ]; then
+            echo "failed to copy $interpreter to $cache/partial-$release-$arch/$interpreter_path"
+            return 1
+        fi
+        chroot "$cache/partial-$release-$arch" debootstrap/debootstrap --second-stage
+        if [ $? -ne 0 ]; then
+            echo "failed to update the rootfs, aborting"
+            return 1
+        fi
     fi
 
     mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch"
@@ -323,6 +369,8 @@ install_debian()
     release=$2
     arch=$3
     cache="$4/debian"
+    interpreter="$5"
+    interpreter_path="$6"
     mkdir -p $LOCALSTATEDIR/lock/subsys/
     (
         flock -x 9
@@ -333,7 +381,7 @@ install_debian()
 
         echo "Checking cache download in $cache/rootfs-$release-$arch ... "
         if [ ! -e "$cache/rootfs-$release-$arch" ]; then
-            download_debian $cache $arch $release
+            download_debian $cache $arch $release "$interpreter" "$interpreter_path"
             if [ $? -ne 0 ]; then
                 echo "Failed to download 'debian base'"
                 return 1
@@ -388,6 +436,7 @@ copy_configuration()
 lxc.tty = $num_tty
 lxc.utsname = $hostname
 lxc.arch = $arch
+lxc.pts=1023
 EOF
 
     if [ $? -ne 0 ]; then
@@ -404,6 +453,7 @@ post_process()
     local release="$1"; shift
     local arch="$1"; shift
     local hostarch="$1"; shift
+    local interpreter="$1"; shift
     local packages="$*"
 
     # Disable service startup
@@ -414,7 +464,7 @@ EOF
     chmod +x ${rootfs}/usr/sbin/policy-rc.d
 
     # If the container isn't running a native architecture, setup multiarch
-    if [ "${arch}" != "${hostarch}" ]; then
+    if [ "$interpreter" = "" -a "${arch}" != "${hostarch}" ]; then
         # Test if dpkg supports multiarch
         if ! chroot $rootfs dpkg --print-foreign-architecture 2>&1; then
             chroot $rootfs dpkg --add-architecture ${hostarch}
@@ -423,7 +473,7 @@ EOF
 
     # Write a new sources.list containing both native and multiarch entries
     > ${rootfs}/etc/apt/sources.list
-    if [ "${arch}" = "${hostarch}" ]; then
+    if [ "$interpreter" != "" -a "${arch}" = "${hostarch}" ]; then
         write_sourceslist ${rootfs} ${release} ${arch}
     else
         write_sourceslist ${rootfs} ${release}
@@ -492,6 +542,7 @@ Template specific options can be passed to lxc-create after a '--' like this:
 Usage: $1 -h|--help -p|--path=<path> [-c|--clean] [-a|--arch=<arch>] [-r|--release=<release>]
                                      [--mirror=<mirror>] [--security-mirror=<security mirror>]
                                      [--package=<package_name1,package_name2,...>]
+                                     [-I|--interpreter-path=<interpreter path>]
 
 Options :
 
@@ -510,6 +561,8 @@ Options :
                          List of additional packages to install. Comma separated, without space.
   -c, --clean            only clean up the cache and terminate
   --enable-non-free      include also Debian's contrib and non-free repositories.
+  -I|--interpreter-path=INTERPRETER-PATH
+                         Path of the binfmt interpreter to copy to the rootfs
 
 Environment variables:
 
@@ -522,7 +575,7 @@ EOF
     return 0
 }
 
-options=$(getopt -o hp:n:a:r:c -l arch:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror: -- "$@")
+options=$(getopt -o hp:n:a:r:cI: -l arch:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror:,interpreter-path: -- "$@")
 if [ $? -ne 0 ]; then
         usage $(basename $0)
         exit 1
@@ -547,6 +600,8 @@ do
            --)                shift 1; break ;;
 
         -a|--arch)            arch=$2; shift 2;;
+        -I|--interpreter-path)
+                              interpreter="$2"; shift 2;;
         -c|--clean)           clean=1; shift 1;;
            --enable-non-free) mainonly=0; shift 1;;
            --mirror)          MIRROR=$2; shift 2;;
@@ -573,20 +628,32 @@ if [ "$arch" = "x86_64" ]; then
     arch=amd64
 fi
 
-if [ $hostarch = "i386" -a $arch = "amd64" ]; then
-    echo "can't create $arch container on $hostarch"
-    exit 1
-fi
+if [ "$interpreter" = "" ] ; then
+    if [ $hostarch = "i386" -a $arch = "amd64" ]; then
+        echo "can't create $arch container on $hostarch"
+        exit 1
+    fi
 
-if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \
-   [ $arch != "armhf" -a $arch != "armel" ]; then
-    echo "can't create $arch container on $hostarch"
-    exit 1
-fi
+    if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \
+       [ $arch != "armhf" -a $arch != "armel" ]; then
+        echo "can't create $arch container on $hostarch"
+        exit 1
+    fi
 
-if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then
-    echo "can't create $arch container on $hostarch"
-    exit 1
+    if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then
+        echo "can't create $arch container on $hostarch"
+        exit 1
+    fi
+else
+    if ! file -b "${interpreter}" |grep -q "statically linked" ; then
+        echo "'${interpreter}' must be statically linked" 1>&2
+        exit 1
+    fi
+    interpreter_path=$(find_interpreter "$interpreter")
+    if [ $? -ne 0 ] ; then
+        echo "no binfmt interpreter using $(basename $interpreter)" 1>&2
+        exit 1
+    fi
 fi
 
 type debootstrap
@@ -630,7 +697,7 @@ else
     num_tty=4
 fi
 
-install_debian $rootfs $release $arch $LXC_CACHE_PATH
+install_debian $rootfs $release $arch $LXC_CACHE_PATH "$interpreter" "$interpreter_path"
 if [ $? -ne 0 ]; then
     echo "failed to install debian"
     exit 1
@@ -650,7 +717,7 @@ fi
 
 configure_debian_systemd $path $rootfs $config $num_tty
 
-post_process ${rootfs} ${release} ${arch} ${hostarch} ${packages}
+post_process ${rootfs} ${release} ${arch} ${hostarch} "${interpreter}" ${packages}
 
 if [ ! -z "$clean" ]; then
     clean || exit 1
-- 
2.5.5



More information about the lxc-devel mailing list