diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1881bb6..f16a1a8 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,8 +1,12 @@ -Make sure you are running the latest version of Anbox before reporting an issue. +1. Please check that no similar bug is already reported. Have a look on the list of open bugs at https://github.com/anbox/anbox/issues +2. Make sure you are running the latest version of Anbox before reporting an issue. Update snap to latest: `snap refresh --devmode --edge anbox` +3. Make sure you have debug logs enabled: +`sudo snap set anbox debug.enable=true` +4. Reproduce the error while debug logs enabled. +5. Run the anbox logs collection utility and attach the tar file. +`sudo /snap/bin/anbox.collect-bug-info ` -Please also check that no similar bug is already reported. Have a look on the list of open bugs at https://github.com/anbox/anbox/issues - -** Please paste the result of `anbox system-info` below:** +6. ** Please paste the result of `anbox system-info` below:** ``` [please paste printout of `anbox system-info` here] ``` @@ -14,3 +18,4 @@ Please also check that no similar bug is already reported. Have a look on the li **Additional info:** + diff --git a/.gitignore b/.gitignore index a9b4b45..0a290ca 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ build*/ parts/ stage/ prime/ -snap/ android-images/ *.snap CMakeLists.txt.user diff --git a/docs/install.md b/docs/install.md index 1735de8..b194673 100644 --- a/docs/install.md +++ b/docs/install.md @@ -104,3 +104,23 @@ a more stable and bug free experience. Once proper confinement for the Anbox snap exists we will also start using the candidate and stable channels. + +# Uninstall Anbox + +If you want to remove Anbox from your system you first have to remove the snap: + +**NOTE:** By removing the snap you remove all data you stored within the snap +from your system. There is no way to bring it back. + +``` + $ snap remove anbox +``` + +Once the snap is removed you have to remove the installed kernel modules as well: + +``` + $ sudo apt install ppa-purge + $ sudo ppa-purge ppa:morphis/anbox-support +``` + +Once done Anbox is removed from your system. diff --git a/external/backtrace-cpp/CMakeLists.txt b/external/backtrace-cpp/CMakeLists.txt index c9ae155..b31bb8c 100644 --- a/external/backtrace-cpp/CMakeLists.txt +++ b/external/backtrace-cpp/CMakeLists.txt @@ -38,7 +38,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) endif() # ANBOX: allow old-style casts -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=old-style-cast") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=old-style-cast -Wno-error=switch-default") ############################################################################### # BACKWARD OBJECT diff --git a/scripts/collect-bug-info.sh b/scripts/collect-bug-info.sh index 6526c6d..199058d 100755 --- a/scripts/collect-bug-info.sh +++ b/scripts/collect-bug-info.sh @@ -24,8 +24,8 @@ echo "Collecting anbox log files ... " set -x # Collect several things which are of interest for bug reports cp /var/snap/anbox/common/data/system.log $TMPDIR || true -cp /var/snap/anbox/containers/lxc-monitord.log $TMPDIR || true -cp /var/snap/anbox/logs/container.log $TMPDIR || true +cp /var/snap/anbox/common/containers/lxc-monitord.log $TMPDIR || true +cp /var/snap/anbox/common/logs/container.log $TMPDIR || true $SNAP/command-anbox.wrapper system-info > $TMPDIR/system-info.log 2>&1 || true if [ -e /etc/systemd/system/snap.anbox.container-manager.service ]; then diff --git a/scripts/container-manager.sh b/scripts/container-manager.sh index a0fec05..7e7fb15 100755 --- a/scripts/container-manager.sh +++ b/scripts/container-manager.sh @@ -43,6 +43,10 @@ start() { $SNAP/sbin/apparmor_parser -r $SNAP/apparmor/anbox-container.aa fi + if [ -e "$SNAP_COMMON"/.enable_debug ]; then + export ANBOX_LOG_LEVEL=debug + fi + exec $AA_EXEC $SNAP/bin/anbox-wrapper.sh container-manager \ --data-path=$DATA_PATH \ --android-image=$ANDROID_IMG \ diff --git a/scripts/snap-wrapper.sh b/scripts/snap-wrapper.sh index 60cb85e..7524225 100755 --- a/scripts/snap-wrapper.sh +++ b/scripts/snap-wrapper.sh @@ -29,4 +29,8 @@ export XDG_DATA_HOME="$SNAP_USER_COMMON/app-data" # configured but the actual EGL implementation is missing. export __EGL_VENDOR_LIBRARY_DIRS="$SNAP/glvnd" +if [ -e "$SNAP_COMMON"/.enable_debug ]; then + export ANBOX_LOG_LEVEL=debug +fi + exec $SNAP/usr/bin/anbox $@ diff --git a/snap/hooks/configure b/snap/hooks/configure new file mode 100755 index 0000000..89dea03 --- /dev/null +++ b/snap/hooks/configure @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "$(snapctl get debug.enable)" = true ]; then + touch "$SNAP_COMMON"/.enable_debug +else + rm -f "$SNAP_COMMON"/.enable_debug +fi + +exit 0 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 2602396..377455a 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -72,6 +72,7 @@ parts: x86_64) IMAGE_PATH="2018/05/23" IMAGE_NAME="android_amd64.img" + IMAGE_HASH="cbcb8c4740ed38dbc243122df2d8d87511a9c8dcc162781f2eabb5dc1ea079fe" ;; *) echo "ERROR: Unknown architecture $ARCH" @@ -83,6 +84,10 @@ parts: # expects the downloaded file to be an archive it can extract. echo "Downloading image..." wget http://build.anbox.io/android-images/$IMAGE_PATH/$IMAGE_NAME + + echo "$IMAGE_HASH $IMAGE_NAME" > image-hash + sha256sum -c image-hash + mv $IMAGE_NAME $SNAPCRAFT_PART_INSTALL/android.img fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 44abfd3..37518d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,6 +53,9 @@ add_library(anbox-protobuf ${GENERATED_PROTOBUF_RPC_HDRS} ${GENERATED_PROTOBUF_CONTAINER_SRCS} ${GENERATED_PROTOBUF_CONTAINER_HDRS} + anbox/protobuf/anbox_rpc.proto + anbox/protobuf/anbox_bridge.proto + anbox/protobuf/anbox_container.proto anbox/protobuf/google_protobuf_guard.cpp) target_link_libraries(anbox-protobuf ${PROTOBUF_LITE_LIBRARIES}) diff --git a/src/anbox/cmds/session_manager.cpp b/src/anbox/cmds/session_manager.cpp index e53af53..4518382 100644 --- a/src/anbox/cmds/session_manager.cpp +++ b/src/anbox/cmds/session_manager.cpp @@ -244,9 +244,13 @@ anbox::cmds::SessionManager::SessionManager() {bridge_connector->socket_file(), "/dev/anbox_bridge"}, {audio_server->socket_file(), "/dev/anbox_audio"}, {SystemConfiguration::instance().input_device_dir(), "/dev/input"}, - {"/dev/binder", "/dev/binder"}, - {"/dev/ashmem", "/dev/ashmem"}, - {"/dev/fuse", "/dev/fuse"}, + + }; + + container_configuration.devices = { + {"/dev/binder"}, + {"/dev/ashmem"}, + {"/dev/fuse"}, }; dispatcher->dispatch([&]() { diff --git a/src/anbox/container/configuration.h b/src/anbox/container/configuration.h index 7e62ad4..99757cf 100644 --- a/src/anbox/container/configuration.h +++ b/src/anbox/container/configuration.h @@ -18,13 +18,15 @@ #ifndef ANBOX_CONTAINER_CONFIGURATION_H_ #define ANBOX_CONTAINER_CONFIGURATION_H_ -#include #include +#include +#include namespace anbox { namespace container { struct Configuration { - std::map bind_mounts; + std::unordered_map bind_mounts; + std::vector devices; }; } // namespace container } // namespace anbox diff --git a/src/anbox/container/lxc_container.cpp b/src/anbox/container/lxc_container.cpp index d042dde..efb70fa 100644 --- a/src/anbox/container/lxc_container.cpp +++ b/src/anbox/container/lxc_container.cpp @@ -44,6 +44,14 @@ constexpr const char *default_container_ip_address{"192.168.250.2"}; constexpr const std::uint32_t default_container_ip_prefix_length{24}; constexpr const char *default_host_ip_address{"192.168.250.1"}; constexpr const char *default_dns_server{"8.8.8.8"}; + +constexpr int device_major(__dev_t dev) { + return int(((dev >> 8) & 0xfff) | ((dev >> 32) & (0xfffff000))); +} + +constexpr int device_minor(__dev_t dev) { + return int((dev & 0xff) | ((dev >> 12) & (0xffffff00))); +} } // namespace namespace anbox { @@ -157,9 +165,63 @@ void LxcContainer::setup_network() { } } +void LxcContainer::add_device(const std::string& device) { + struct stat st; + int r = stat(device.c_str(), &st); + if (r < 0) { + const auto msg = utils::string_format("Failed to retrieve information about device %s", device); + throw std::runtime_error(msg); + } + + const auto major = device_major(st.st_rdev); + const auto minor = device_minor(st.st_rdev); + const auto mode = st.st_mode; + const auto new_device_name = fs::basename(device); + const auto devices_path = fs::path(SystemConfiguration::instance().container_devices_dir()); + const auto new_device_path = (devices_path / new_device_name).string(); + + const auto encoded_device_number = (minor & 0xff) | (major << 8) | ((minor & !0xff) << 12); + r = mknod(new_device_path.c_str(), mode, encoded_device_number); + if (r < 0) { + auto msg = utils::string_format("Failed to create node for device %s: %s", + device, strerror(errno)); + throw std::runtime_error(msg); + } + + auto base_uid = unprivileged_user_id; + if (privileged_) + base_uid = 0; + + const auto shifted_uid = base_uid + st.st_uid; + const auto shifted_gid = base_uid + st.st_gid; + r = chown(new_device_path.c_str(), shifted_uid, shifted_gid); + if (r < 0) { + auto msg = utils::string_format("Failed to change ownership of new node for %s: %s", + device, strerror(errno)); + throw std::runtime_error(msg); + } + + // Needed as mknod respects the umask + r = chmod(new_device_path.c_str(), mode); + if (r < 0) { + auto msg = utils::string_format("Failed to change mode of new node for %s: %s", + device, strerror(errno)); + throw::std::runtime_error(msg); + } + + auto target_path = device; + // Strip a leading slash as LXC doesn't like that + if (utils::string_starts_with(device, "/")) + target_path = device.substr(1, device.length() - 1); + + const auto entry = utils::string_format("%s %s none bind,create=file,optional 0 0", + new_device_path, target_path); + set_config_item("lxc.mount.entry", entry); +} + void LxcContainer::start(const Configuration &configuration) { if (getuid() != 0) - BOOST_THROW_EXCEPTION(std::runtime_error("You have to start the container as root")); + throw std::runtime_error("You have to start the container as root"); if (container_ && container_->is_running(container_)) { WARNING("Container already started, stopping it now"); @@ -175,7 +237,7 @@ void LxcContainer::start(const Configuration &configuration) { container_ = lxc_container_new("default", container_config_dir.c_str()); if (!container_) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create LXC container instance")); + throw std::runtime_error("Failed to create LXC container instance"); // If container is still running (for example after a crash) we stop it here // to ensure its configuration is synchronized. @@ -220,20 +282,11 @@ void LxcContainer::start(const Configuration &configuration) { setup_id_maps(); auto bind_mounts = configuration.bind_mounts; - - // Extra bind-mounts for user-namespace setup - bind_mounts.insert({"/dev/console", "dev/console"}); - bind_mounts.insert({"/dev/full", "dev/full"}); - bind_mounts.insert({"/dev/null", "dev/null"}); - bind_mounts.insert({"/dev/random", "dev/random"}); - bind_mounts.insert({"/dev/tty", "dev/tty"}); - bind_mounts.insert({"/dev/urandom", "dev/urandom"}); - bind_mounts.insert({"/dev/zero", "dev/zero"}); - for (const auto &bind_mount : bind_mounts) { std::string create_type = "file"; - if (fs::is_directory(bind_mount.first)) create_type = "dir"; + if (fs::is_directory(bind_mount.first)) + create_type = "dir"; auto target_path = bind_mount.second; // The target path needs to be absolute and pointing to the right @@ -243,18 +296,36 @@ void LxcContainer::start(const Configuration &configuration) { target_path = std::string("/") + target_path; target_path = rootfs_path + target_path; - set_config_item( - "lxc.mount.entry", - utils::string_format("%s %s none bind,create=%s,optional 0 0", - bind_mount.first, target_path, create_type)); + const auto entry = utils::string_format("%s %s none bind,create=%s,optional 0 0", + bind_mount.first, target_path, create_type); + set_config_item("lxc.mount.entry", entry); } - if (!container_->save_config(container_, nullptr)) - BOOST_THROW_EXCEPTION( - std::runtime_error("Failed to save container configuration")); + auto devices = configuration.devices; - if (not container_->start(container_, 0, nullptr)) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start container")); + // Additional devices we need in our container + devices.push_back("/dev/console"); + devices.push_back("/dev/full"); + devices.push_back("/dev/null"); + devices.push_back("/dev/random"); + devices.push_back("/dev/tty"); + devices.push_back("/dev/urandom"); + devices.push_back("/dev/zero"); + + // Remove all left over devices from last time first before + // creating any new ones + const auto devices_dir = SystemConfiguration::instance().container_devices_dir(); + fs::remove_all(devices_dir); + fs::create_directories(devices_dir); + + for (const auto& device : devices) + add_device(device); + + if (!container_->save_config(container_, nullptr)) + throw std::runtime_error("Failed to save container configuration"); + + if (!container_->start(container_, 0, nullptr)) + throw std::runtime_error("Failed to start container"); state_ = Container::State::running; @@ -262,11 +333,11 @@ void LxcContainer::start(const Configuration &configuration) { } void LxcContainer::stop() { - if (not container_ || not container_->is_running(container_)) + if (!container_ || !container_->is_running(container_)) return; - if (not container_->stop(container_)) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to stop container")); + if (!container_->stop(container_)) + throw std::runtime_error("Failed to stop container"); state_ = Container::State::inactive; @@ -275,8 +346,10 @@ void LxcContainer::stop() { void LxcContainer::set_config_item(const std::string &key, const std::string &value) { - if (!container_->set_config_item(container_, key.c_str(), value.c_str())) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to configure LXC container")); + if (!container_->set_config_item(container_, key.c_str(), value.c_str())) { + const auto msg = utils::string_format("Failed to set config item %s", key); + throw std::runtime_error(msg); + } } Container::State LxcContainer::state() { return state_; } diff --git a/src/anbox/container/lxc_container.h b/src/anbox/container/lxc_container.h index bc87760..1a2c6cd 100644 --- a/src/anbox/container/lxc_container.h +++ b/src/anbox/container/lxc_container.h @@ -40,6 +40,7 @@ class LxcContainer : public Container { void set_config_item(const std::string &key, const std::string &value); void setup_id_maps(); void setup_network(); + void add_device(const std::string& device); State state_; lxc_container *container_; diff --git a/src/anbox/container/management_api_skeleton.cpp b/src/anbox/container/management_api_skeleton.cpp index 3374414..eed820c 100644 --- a/src/anbox/container/management_api_skeleton.cpp +++ b/src/anbox/container/management_api_skeleton.cpp @@ -50,8 +50,12 @@ void ManagementApiSkeleton::start_container( const auto configuration = request->configuration(); for (int n = 0; n < configuration.bind_mounts_size(); n++) { const auto bind_mount = configuration.bind_mounts(n); - container_configuration.bind_mounts.insert( - {bind_mount.source(), bind_mount.target()}); + container_configuration.bind_mounts.insert({bind_mount.source(), bind_mount.target()}); + } + + for (int n = 0; n < configuration.devices_size(); n++) { + const auto device = configuration.devices(n); + container_configuration.devices.push_back(device); } try { diff --git a/src/anbox/container/management_api_stub.cpp b/src/anbox/container/management_api_stub.cpp index ebe7632..6c8bb99 100644 --- a/src/anbox/container/management_api_stub.cpp +++ b/src/anbox/container/management_api_stub.cpp @@ -47,6 +47,11 @@ void ManagementApiStub::start_container(const Configuration &configuration) { message.set_allocated_configuration(message_configuration); + for (const auto &device : configuration.devices) { + auto d = message_configuration->add_devices(); + *d = device; + } + { std::lock_guard lock(mutex_); c->wh.expect_result(); diff --git a/src/anbox/protobuf/anbox_container.proto b/src/anbox/protobuf/anbox_container.proto index 05cbee9..7cc1542 100644 --- a/src/anbox/protobuf/anbox_container.proto +++ b/src/anbox/protobuf/anbox_container.proto @@ -8,6 +8,7 @@ message Configuration { required string target = 2; } repeated BindMount bind_mounts = 1; + repeated string devices = 2; } message StartContainer { diff --git a/src/anbox/system_configuration.cpp b/src/anbox/system_configuration.cpp index 0d3d8ed..9380fe4 100644 --- a/src/anbox/system_configuration.cpp +++ b/src/anbox/system_configuration.cpp @@ -75,6 +75,10 @@ std::string anbox::SystemConfiguration::container_socket_path() const { return path; } +std::string anbox::SystemConfiguration::container_devices_dir() const { + return (data_path / "devices").string(); +} + std::string anbox::SystemConfiguration::socket_dir() const { static std::string dir = anbox::utils::string_format("%s/anbox/sockets", runtime_dir()); return dir; diff --git a/src/anbox/system_configuration.h b/src/anbox/system_configuration.h index e0526ec..d203660 100644 --- a/src/anbox/system_configuration.h +++ b/src/anbox/system_configuration.h @@ -38,6 +38,7 @@ class SystemConfiguration { std::string socket_dir() const; std::string container_config_dir() const; std::string container_socket_path() const; + std::string container_devices_dir() const; std::string input_device_dir() const; std::string application_item_dir() const; std::string resource_dir() const;