diff --git a/.gitignore b/.gitignore index aa90563..bf05908 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ build*/ +parts/ +stage/ +prime/ +*.snap CMakeLists.txt.user diff --git a/README.md b/README.md index 1395920..4c585a2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,53 @@ For more details have a look at the following documentation pages: * The Android "qemud" multiplexing daemon (https://goo.gl/DeYa5J) * Android Qemud services (https://goo.gl/W8Lx6t) +## Installation + +Anbox is available as a snap in the public Ubuntu Store. Currently it +is only available in the edge channel and requires to be installed in +devmode as we don't have proper confinement for it in place yet. + +Additionally you need to manually load the binder and ashmem kernel +drivers everytime as we build them out-of-tree with a hack as this +isn't officially supported. Before you start anbox you always need +to execute + +$ cd anbox +$ scripts/load-kmods.sh + +Anbox can be installed from the Ubuntu Store with + +$ snap install --edge --devmode anbox + +Afterwards run it with + +$ anbox + +After the first installation the container management service needs +a few minutes to setup the container the first time before it is +available. + +Applications can be launched via the launch subcommand of the anbox +binary. For example + +$ anbox launch --package com.android.settings + +## Build from source + +To build the Anbox runtime itself there is nothing special to know +about. We're using cmake as build system. + +$ mkdir build +$ cd build +$ cmake .. +$ make + +That will build the whole stack. A simple + +$ make install + +will install the necessary bits into your system. + ## Copyright and Licensing Anbox reuses code from other projects like the Android Qemu emulator diff --git a/create-click.sh b/create-click.sh deleted file mode 100755 index 3e11618..0000000 --- a/create-click.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -set -e -set -x - -if [ ! -e android-rootfs.tar ] ; then - echo "ERROR: Missing Android rootfs package!" - exit 1 -fi - -./cross-compile-chroot.sh -a armhf -d vivid - -if [ -e click ] ; then - rm -rf click -fi - -mkdir click -cp data/manifest.json click/ -cp data/apparmor.json click/ -cp data/anbox.desktop click/ - -(cd click ; tar xf ../android-rootfs.tar ; mv rootfs android-rootfs) - -cp build-armhf-vivid/src/anbox click/ -cp build-armhf-vivid/src/anbox-container click/ - -(cd click ; click build .) diff --git a/scripts/anbox-init.sh b/scripts/anbox-init.sh index 55badda..ce61cce 100644 --- a/scripts/anbox-init.sh +++ b/scripts/anbox-init.sh @@ -23,34 +23,6 @@ function prepare_filesystem() { chown system:system /dev/$f chmod 0666 /dev/$f done - - if [ ! -e /.anbox_setup_done ] ; then - echo "Fixing up all permissions ..." - - # Fixup permissions of the android binaries in /system - while read line - do - file=`echo $line | cut -d' ' -f 1` - user=`echo $line | cut -d' ' -f 2` - group=`echo $line | cut -d' ' -f 3` - mode=`echo $line | cut -d' ' -f 4` - # Avoid changing symlinks - if [ ! -h /$file ] ; then - chmod $mode /$file - chown -h $user:$group /$file - fi - done < "/filesystem_config.txt" - - # Additional ones not listed in the config generated from the build - for f in qemu_pipe qemu_trace goldfish_pipe ; do - [ ! -e /dev/$f ] && continue - chown system:system /dev/$f - chmod 0666 /dev/$f - done - - echo "Setup done!" - echo $VERSION > /.anbox_setup_done - fi } prepare_filesystem & diff --git a/scripts/container-manager.sh b/scripts/container-manager.sh index 6798363..f923209 100755 --- a/scripts/container-manager.sh +++ b/scripts/container-manager.sh @@ -5,27 +5,56 @@ # Other than that nothing should ever modify the content of the # rootfs. -ROOTFS_PATH=$SNAP_COMMON/var/lib/anbox/rootfs -ROOTFS_VERSION=1 +DATA_PATH=$SNAP_COMMON/var/lib/anbox +ROOTFS_PATH=$DATA_PATH/rootfs +RAMDISK_PATH=$DATA_PATH/ramdisk +INITRD=$SNAP/ramdisk.img +SYSTEM_IMG=$SNAP/system.img +ANDROID_DATA_PATH=$DATA_PATH/android-data -if [ ! -e $ROOTFS_PATH ] || [ "$ROOTFS_VERSION" != "$(cat $ROOTFS_PATH/.version)" ] ; then - rm -rf $ROOTFS_PATH - echo "Copying rootfs into $ROOTFS_PATH .." - mkdir -p $ROOTFS_PATH - tar xf $SNAP/android-rootfs.tar -C $ROOTFS_PATH/ --strip-components=1 - echo $ROOTFS_VERSION > $ROOTFS_PATH/.version +if [ ! -e $INITRD ]; then + echo "ERROR: boot ramdisk does not exist" + exit 1 fi -# Load binder and ashmem kernel drivers. This will just horrible break -# if kernel versions are changing ... -insmod $SNAP/binder_linux.ko || true -chmod 666 /dev/binder -insmod $SNAP/ashmem_linux.ko || true -chmod 666 /dev/ashmem +if [ ! -e $SYSTEM_IMG ]; then + echo "ERROR: system image does not exist" + exit 1 +fi + +# Extract ramdisk content instead of trying to bind mount the +# cpio image file to allow modifications. +rm -Rf $RAMDISK_PATH +mkdir -p $RAMDISK_PATH +cd $RAMDISK_PATH +cat $INITRD | gzip -d | cpio -i + +# FIXME those things should be fixed in the build process +chmod +x $RAMDISK_PATH/anbox-init.sh + +# Setup the read-only rootfs +mkdir -p $ROOTFS_PATH +mount -o bind,ro $RAMDISK_PATH $ROOTFS_PATH +mount -o loop,ro $SYSTEM_IMG $ROOTFS_PATH/system + +# ... but we keep /data in the read/write space +mkdir -p $ANDROID_DATA_PATH +mount -o bind $ANDROID_DATA_PATH $ROOTFS_PATH/data # Make sure our setup path for the container rootfs # is present as lxc is statically configured for # this path. mkdir -p $SNAP_COMMON/lxc -exec $SNAP/bin/anbox-wrapper.sh container-manager +# We start the bridge here as long as a oneshot service unit is not +# possible. See snapcraft.yaml for further details. +$SNAP/bin/anbox-bridge.sh start + +$SNAP/usr/sbin/aa-exec -p unconfined -- $SNAP/bin/anbox-wrapper.sh container-manager +pid=$! +waitpid $pid + +$SNAP/bin/anbox-bridge.sh stop + +umount $ROOTFS_PATH/system +umount $ROOTFS_PATH/data diff --git a/scripts/container-shell.sh b/scripts/container-shell.sh deleted file mode 100755 index 2f94bb9..0000000 --- a/scripts/container-shell.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -$SNAP/bin/lxc-attach \ - --lxcpath=$SNAP_COMMON/var/lib/anbox/containers \ - --name default \ - --clear-env \ - --set-var PATH=/system/bin:/system/sbin:/system/xbin \ - --set-var ANDROID_DATA=/data \ - --set-var ANDROID_ROOT=/system \ - -- \ - /system/bin/sh diff --git a/scripts/iptables-wrapper b/scripts/iptables-wrapper deleted file mode 100755 index 73e9421..0000000 --- a/scripts/iptables-wrapper +++ /dev/null @@ -1,2 +0,0 @@ -#!/system/bin/sh -echo "Dummy iptables wrapper" diff --git a/scripts/launch-abox.sh b/scripts/launch-abox.sh deleted file mode 100755 index b37cdf5..0000000 --- a/scripts/launch-abox.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -e -set -x - -basepath=/home/phablet/android-box -rootfs=$basepath/rootfs -rootfs_overrides=$basepath/overrides -ramdisk=$basepath/ramdisk.img -systemdisk=$basepath/system.img -init_cmd="/init" - -if [ "$1" = "shell" ] ; then - init_cmd=/system/bin/sh -fi - -if [ -d $rootfs ] ; then - sudo umount --recursive $rootfs || true - rm -rf $rootfs -fi - -mkdir -p $rootfs -sudo mount -t tmpfs none $rootfs -(cd $rootfs ; cat $ramdisk | gzip -d | cpio -i) - -mkdir -p $rootfs/dev/pts -sudo mount -o ro,loop $systemdisk $rootfs/system - -if [ -d "$rootfs_overrides" ] ; then - for f in `ls $rootfs_overrides` ; do - if [ "$f" = "system" ] ; then - for f2 in `find $rootfs_overrides/system -type f` ; do - real_path=`echo $f2 | sed -e s:$rootfs_overrides::g` - sudo mount -o bind $f2 $rootfs/$real_path - done - else - cp $rootfs_overrides/$f $rootfs - fi - done -fi - -/home/phablet/bwrap \ - --ro-bind $rootfs / \ - --bind /home/phablet/android-box/iptables-wrapper /system/bin/iptables \ - --bind /home/phablet/android-box/iptables-wrapper /system/bin/ip6tables \ - --dev /dev \ - --proc /proc \ - --tmpfs /data \ - --unshare-user \ - --unshare-ipc \ - --unshare-pid \ - --unshare-net \ - --unshare-uts \ - --uid 0 \ - --gid 0 \ - --setenv PATH /system/bin:/system/sbin:/system/xbin \ - --chdir / \ - $init_cmd - -sudo umount --recursive $rootfs -rm -rf $rootfs diff --git a/scripts/setup-rootfs.sh b/scripts/setup-rootfs.sh deleted file mode 100755 index f83b6e6..0000000 --- a/scripts/setup-rootfs.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# We need to put the rootfs somewhere where we can modify some -# parts of the content on first boot (namely file permissions). -# Other than that nothing should ever modify the content of the -# rootfs. - -ROOTFS_PATH=$SNAP_COMMON/var/lib/anbox/rootfs - -if [ -d $ROOTFS_PATH ] ; then - rm -rf $ROOTFS_PATH -fi - -echo "Copying rootfs into $ROOTFS_PATH .." -mkdir -p $ROOTFS_PATH -tar xf $SNAP/android-rootfs.tar -C $ROOTFS_PATH/ --strip-components=1 diff --git a/scripts/shell.sh b/scripts/shell.sh deleted file mode 100755 index 89d1294..0000000 --- a/scripts/shell.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -exec /bin/bash $@ diff --git a/snapcraft.yaml b/snapcraft.yaml index bd76cee..95abac0 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -10,21 +10,24 @@ grade: devel apps: anbox: - command: bin/anbox-wrapper.sh run + command: bin/anbox-wrapper.sh container-manager: command: bin/container-manager.sh daemon: simple - bridge: - command: bin/anbox-bridge.sh start - stop-command: bin/anbox-bridge.sh stop - daemon: oneshot +# FIXME: a oneshot unit with start/stop commands needs also RemainAfterExit=yes +# but this isn't supported by snapd yet. See LP #1647169 for details. +# bridge: +# command: bin/anbox-bridge.sh start +# stop-command: bin/anbox-bridge.sh stop +# daemon: oneshot parts: - android-rootfs: + android: plugin: copy source: . files: - android-rootfs.tar: android-rootfs.tar + system.img: system.img + ramdisk.img: ramdisk.img anbox-common: plugin: copy source: . @@ -36,6 +39,10 @@ parts: - bin/anbox-bridge.sh - bin/anbox-wrapper.sh - bin/container-manager.sh + apparmor: + plugin: nil + stage-packages: + - apparmor lxc: source: git://github.com/morphis/lxc source-branch: snappy-support diff --git a/src/anbox/container/lxc_container.cpp b/src/anbox/container/lxc_container.cpp index ab721bd..4167a1a 100644 --- a/src/anbox/container/lxc_container.cpp +++ b/src/anbox/container/lxc_container.cpp @@ -113,9 +113,11 @@ void LxcContainer::start(const Configuration &configuration) { "lxc.logfile", utils::string_format("%s/container.log", config::log_path()).c_str()); - set_config_item("lxc.network.type", "veth"); - set_config_item("lxc.network.flags", "up"); - set_config_item("lxc.network.link", "anboxbr0"); + if (fs::exists("/sys/class/net/anboxbr0")) { + set_config_item("lxc.network.type", "veth"); + set_config_item("lxc.network.flags", "up"); + set_config_item("lxc.network.link", "anboxbr0"); + } #if 0 // Android uses namespaces as well so we have to allow nested namespaces for LXC @@ -136,9 +138,12 @@ void LxcContainer::start(const Configuration &configuration) { if (fs::is_directory(bind_mount.first)) create_type = "dir"; auto target_path = bind_mount.second; - // LXC wants target paths relative to the container rootfs so - // prividing an absolute path doesn't work. - if (utils::string_starts_with(target_path, "/")) target_path.erase(0, 1); + // The target path needs to be absolute and pointing to the right + // location inside the target rootfs as otherwise we get problems + // when running in confined environments like snap's. + if (!utils::string_starts_with(target_path, "/")) + target_path = std::string("/") + target_path; + target_path = config::rootfs_path() + target_path; set_config_item( "lxc.mount.entry", diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp index 69fb079..80e77de 100644 --- a/src/anbox/daemon.cpp +++ b/src/anbox/daemon.cpp @@ -42,7 +42,10 @@ Daemon::Daemon() } int Daemon::Run(const std::vector &arguments) try { - return cmd.run({std::cin, std::cout, arguments}); + auto argv = arguments; + if (arguments.size() == 0) + argv = {"run"}; + return cmd.run({std::cin, std::cout, argv}); } catch (std::exception &err) { ERROR("%s", err.what()); return EXIT_FAILURE;