From 2b25f6f5bbf9ae9ada2dcf23ce2c62dee46019bf Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Sun, 14 Aug 2016 15:48:21 +0200 Subject: [PATCH] Rework container management * based on liblxc now * container is managed by a management process which needs to run as root. LXC will take care of strict confinement of this container. We still don't allow any direct hardware access. * `anbox shell` command is gone. Same functionality can be archive with the lxc-attach command. --- scripts/anbox-init.sh | 4 + src/CMakeLists.txt | 24 +-- src/anbox/bridge/android_api_stub.cpp | 2 +- src/anbox/cmds/container_manager.cpp | 45 +++++ .../container_manager.h} | 24 ++- src/anbox/cmds/run.cpp | 52 +----- src/anbox/cmds/run.h | 1 - src/anbox/cmds/start_container.cpp | 49 +++++ src/anbox/cmds/{shell.h => start_container.h} | 11 +- src/anbox/config.cpp | 103 +++++++++-- src/anbox/config.h | 11 +- src/anbox/container.cpp | 125 ------------- src/anbox/container.h | 65 ------- .../{cmds/shell.cpp => container/client.cpp} | 29 ++- src/anbox/container/client.h | 37 ++++ src/anbox/container/container.cpp | 25 +++ src/anbox/container/container.h | 47 +++++ src/anbox/container/lxc_container.cpp | 169 ++++++++++++++++++ src/anbox/container/lxc_container.h | 47 +++++ src/anbox/container/service.cpp | 112 ++++++++++++ src/anbox/container/service.h | 58 ++++++ src/anbox/container_connector.cpp | 88 --------- src/anbox/daemon.cpp | 24 +-- src/anbox/daemon.h | 2 - src/anbox/input/device.cpp | 3 +- src/anbox/input/manager.cpp | 11 +- src/anbox/input/manager.h | 2 - src/anbox/network/connections.h | 16 ++ src/anbox/network/credentials.cpp | 40 +++++ src/anbox/network/credentials.h | 43 +++++ src/anbox/network/local_socket_messenger.cpp | 59 ++++++ src/anbox/network/local_socket_messenger.h | 48 +++++ .../network/published_socket_connector.cpp | 65 +------ src/anbox/network/socket_helper.cpp | 86 +++++++++ src/anbox/network/socket_helper.h | 31 ++++ src/anbox/network/socket_messenger.cpp | 12 ++ src/anbox/network/socket_messenger.h | 5 +- .../boot_properties_message_processor.cpp | 2 +- src/anbox/ubuntu/window.cpp | 2 +- src/anbox/ubuntu/window_creator.cpp | 19 +- src/anbox/ubuntu/window_creator.h | 3 +- src/anbox/utils.cpp | 8 + src/anbox/utils.h | 2 + src/container_main.cpp | 8 +- 44 files changed, 1116 insertions(+), 503 deletions(-) create mode 100644 src/anbox/cmds/container_manager.cpp rename src/anbox/{container_connector.h => cmds/container_manager.h} (69%) create mode 100644 src/anbox/cmds/start_container.cpp rename src/anbox/cmds/{shell.h => start_container.h} (84%) delete mode 100644 src/anbox/container.cpp delete mode 100644 src/anbox/container.h rename src/anbox/{cmds/shell.cpp => container/client.cpp} (54%) create mode 100644 src/anbox/container/client.h create mode 100644 src/anbox/container/container.cpp create mode 100644 src/anbox/container/container.h create mode 100644 src/anbox/container/lxc_container.cpp create mode 100644 src/anbox/container/lxc_container.h create mode 100644 src/anbox/container/service.cpp create mode 100644 src/anbox/container/service.h delete mode 100644 src/anbox/container_connector.cpp create mode 100644 src/anbox/network/credentials.cpp create mode 100644 src/anbox/network/credentials.h create mode 100644 src/anbox/network/local_socket_messenger.cpp create mode 100644 src/anbox/network/local_socket_messenger.h create mode 100644 src/anbox/network/socket_helper.cpp create mode 100644 src/anbox/network/socket_helper.h diff --git a/scripts/anbox-init.sh b/scripts/anbox-init.sh index e426773..55badda 100644 --- a/scripts/anbox-init.sh +++ b/scripts/anbox-init.sh @@ -57,5 +57,9 @@ prepare_filesystem & echo "Waiting for filesystem being prepared ..." wait $! +ln -sf /dev/sockets/qemu_pipe /dev/qemu_pipe +ln -sf /dev/sockets/qemud /dev/qemud +ln -sf /dev/sockets/anbox_bridge /dev/anbox_bridge + echo "Starting real init now ..." /init diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d41a6b..1097859 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories( ${DBUS_CPP_INCLUDE_DIRS} ${DBUS_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS} + ${LXC_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src @@ -37,9 +38,6 @@ set(SOURCES anbox/daemon.cpp anbox/config.cpp anbox/not_reachable.cpp - anbox/namespace_attacher.cpp - anbox/container.cpp - anbox/container_connector.cpp anbox/application_manager.h anbox/common/fd.cpp @@ -48,6 +46,12 @@ set(SOURCES anbox/common/wait_handle.cpp anbox/common/dispatcher.cpp + anbox/container/service.cpp + anbox/container/client.cpp + anbox/container/container.cpp + anbox/container/lxc_container.cpp + + anbox/network/credentials.cpp anbox/network/message_sender.h anbox/network/message_receiver.h anbox/network/message_processor.h @@ -59,6 +63,8 @@ set(SOURCES anbox/network/socket_messenger.cpp anbox/network/delegate_message_processor.h anbox/network/delegate_connection_creator.h + anbox/network/local_socket_messenger.cpp + anbox/network/socket_helper.cpp anbox/graphics/opengles_message_processor.cpp anbox/graphics/gl_renderer_server.cpp @@ -101,10 +107,11 @@ set(SOURCES anbox/cmds/version.cpp anbox/cmds/run.cpp - anbox/cmds/shell.cpp anbox/cmds/install_app.cpp anbox/cmds/launch_app.cpp anbox/cmds/reset.cpp + anbox/cmds/container_manager.cpp + anbox/cmds/start_container.cpp anbox/do_not_copy_or_move.h anbox/optional.h @@ -114,10 +121,9 @@ add_library(anbox-core ${SOURCES}) target_link_libraries(anbox-core ${Boost_LDFLAGS} ${Boost_LIBRARIES} - ${MIRCLIENT_LDFLAGS} - ${MIRCLIENT_LIBRARIES} ${DBUS_CPP_LIBRARIES} ${SDL2_LIBRARIES} + ${LXC_LIBRARIES} pthread process-cpp OpenglRender @@ -127,12 +133,8 @@ add_executable(anbox main.cpp) target_link_libraries(anbox anbox-core) -add_executable(anbox-container container_main.cpp) -target_link_libraries(anbox-container - bwrap) - install( - TARGETS anbox anbox-container + TARGETS anbox RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static) diff --git a/src/anbox/bridge/android_api_stub.cpp b/src/anbox/bridge/android_api_stub.cpp index 4a95f5c..9646fc5 100644 --- a/src/anbox/bridge/android_api_stub.cpp +++ b/src/anbox/bridge/android_api_stub.cpp @@ -60,7 +60,7 @@ void AndroidApiStub::install(const std::string &path) { fs::copy(path, target_path); const auto container_path = utils::string_format("%s/%s", - config::container_share_path(), fs::path(path).filename().string()); + config::container_android_share_path(), fs::path(path).filename().string()); auto c = std::make_shared>(); protobuf::bridge::InstallApplication message; diff --git a/src/anbox/cmds/container_manager.cpp b/src/anbox/cmds/container_manager.cpp new file mode 100644 index 0000000..f30c08d --- /dev/null +++ b/src/anbox/cmds/container_manager.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/cmds/container_manager.h" +#include "anbox/container/service.h" +#include "anbox/runtime.h" +#include "anbox/logger.h" + +#include "core/posix/signal.h" + +anbox::cmds::ContainerManager::ContainerManager() + : CommandWithFlagsAndAction{cli::Name{"container-manager"}, cli::Usage{"container-manager"}, cli::Description{"Start the container manager service"}} +{ + action([](const cli::Command::Context& ctxt) { + auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, + core::posix::Signal::sig_int}); + trap->signal_raised().connect([trap](const core::posix::Signal &signal) { + INFO("Signal %i received. Good night.", static_cast(signal)); + trap->stop(); + }); + + auto rt = Runtime::create(); + auto service = container::Service::create(rt); + + rt->start(); + trap->run(); + rt->stop(); + + return 0; + }); +} diff --git a/src/anbox/container_connector.h b/src/anbox/cmds/container_manager.h similarity index 69% rename from src/anbox/container_connector.h rename to src/anbox/cmds/container_manager.h index 10a7d10..8f92ef7 100644 --- a/src/anbox/container_connector.h +++ b/src/anbox/cmds/container_manager.h @@ -15,24 +15,22 @@ * */ -#ifndef ANBOX_CONTAINER_CONNECTOR_H_ -#define ANBOX_CONTAINER_CONNECTOR_H_ +#ifndef ANBOX_CMDS_CONTAINER_MANAGER_H_ +#define ANBOX_CMDS_CONTAINER_MANAGER_H_ +#include +#include #include +#include "anbox/cli.h" + namespace anbox { -class NamespaceAttacher; -class ContainerConnector { +namespace cmds { +class ContainerManager : public cli::CommandWithFlagsAndAction { public: - ContainerConnector(int pid = -1); - ~ContainerConnector(); - - int run(const std::string &path); - -private: - int pid_; - std::shared_ptr namespaces_; + ContainerManager(); }; -} // namespace +} // namespace cmds +} // namespace anbox #endif diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp index aab792a..42a0e9a 100644 --- a/src/anbox/cmds/run.cpp +++ b/src/anbox/cmds/run.cpp @@ -21,7 +21,6 @@ #include "anbox/logger.h" #include "anbox/runtime.h" -#include "anbox/container.h" #include "anbox/config.h" #include "anbox/common/dispatcher.h" #include "anbox/cmds/run.h" @@ -36,6 +35,7 @@ #include "anbox/ubuntu/platform_api_skeleton.h" #include "anbox/ubuntu/window_creator.h" #include "anbox/dbus/skeleton/service.h" +#include "anbox/container/client.h" #include @@ -65,7 +65,6 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) : CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, cli::Description{"Run the the anbox system"}}, bus_factory_(bus_factory) { - flag(cli::make_flag(cli::Name{"rootfs"}, cli::Description{"Path to Android rootfs"}, rootfs_)); // Just for the purpose to allow QtMir (or unity8) to find this on our /proc/*/cmdline // for proper confinement etc. flag(cli::make_flag(cli::Name{"desktop_file_hint"}, cli::Description{"Desktop file hint for QtMir/Unity8"}, desktop_file_hint_)); @@ -75,11 +74,6 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) flag(cli::make_flag(cli::Name{"icon"}, cli::Description{"Icon of the application to run"}, icon_)); action([this](const cli::Command::Context &ctx) { - if (rootfs_.empty() || !fs::is_directory(fs::path(rootfs_))) { - ctx.cout << "Not valid rootfs path provided" << std::endl; - return EXIT_FAILURE; - } - auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, core::posix::Signal::sig_int}); trap->signal_raised().connect([trap](const core::posix::Signal &signal) { @@ -87,6 +81,10 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) trap->stop(); }); + utils::ensure_paths({ + config::socket_path(), + }); + auto rt = Runtime::create(); auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt); @@ -99,14 +97,14 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) // Socket which will be used by the qemud service inside the Android // container for things like sensors, vibrtator etc. auto qemud_connector = std::make_shared( - utils::string_format("%s/qemud", config::data_path()), + utils::string_format("%s/qemud", config::socket_path()), rt, std::make_shared()); // The qemu pipe is used as a very fast communication channel between guest // and host for things like the GLES emulation/translation, the RIL or ADB. auto qemu_pipe_connector = std::make_shared( - utils::string_format("%s/qemu_pipe", config::data_path()), + utils::string_format("%s/qemu_pipe", config::socket_path()), rt, std::make_shared(rt, renderer->socket_path(), @@ -115,7 +113,7 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) auto android_api_stub = std::make_shared(); auto bridge_connector = std::make_shared( - utils::string_format("%s/anbox_bridge", config::data_path()), + utils::string_format("%s/anbox_bridge", config::socket_path()), rt, std::make_shared(rt, [&](const std::shared_ptr &sender) { @@ -148,36 +146,8 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) return std::make_shared(sender, server, pending_calls); })); - auto spec = Container::Spec::Default(); - spec.rootfs_path = rootfs_; - spec.bind_paths.insert({qemud_connector->socket_file(), "/dev/qemud"}); - spec.bind_paths.insert({qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"}); - spec.bind_paths.insert({bridge_connector->socket_file(), "/dev/anbox_bridge"}); + container::Client container(rt); - input_manager->generate_mappings(spec.bind_paths); - - // A place where we can exchange files with the container - spec.bind_paths.insert({config::host_share_path(), config::container_share_path()}); - - spec.bind_paths.insert({ config::host_android_data_path(), "/data" }); - spec.bind_paths.insert({ config::host_android_cache_path(), "/cache" }); - spec.bind_paths.insert({ config::host_android_storage_path(), "/storage" }); - - spec.temporary_dirs.push_back("/dev/input"); - - // NOTE: We're not mapping /dev/alarm here as if its not available - // Android will automatically use its timerfd based fallback - // implementation instead. - - // We isolate the container from accessing binder nodes of the host - // through the IPC namespace which gets support for binder with extra - // patches we require. - spec.dev_bind_paths.push_back("/dev/binder"); - // Required for shared memory allocations. TODO(morphis): Letting the guest - // access should be ok but needs more investigation. - spec.dev_bind_paths.push_back("/dev/ashmem"); - - auto container = Container::create(spec); auto bus = bus_factory_(); bus->install_executor(core::dbus::asio::make_executor(bus, rt->service())); @@ -185,11 +155,7 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub); rt->start(); - container->start(); - trap->run(); - - container->stop(); rt->stop(); return EXIT_SUCCESS; diff --git a/src/anbox/cmds/run.h b/src/anbox/cmds/run.h index f9dd2c2..526c768 100644 --- a/src/anbox/cmds/run.h +++ b/src/anbox/cmds/run.h @@ -38,7 +38,6 @@ public: private: BusFactory bus_factory_; - std::string rootfs_; std::string desktop_file_hint_; std::string apk_; std::string package_; diff --git a/src/anbox/cmds/start_container.cpp b/src/anbox/cmds/start_container.cpp new file mode 100644 index 0000000..3c62cb2 --- /dev/null +++ b/src/anbox/cmds/start_container.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/cmds/start_container.h" +#include "anbox/container/lxc_container.h" +#include "anbox/runtime.h" +#include "anbox/logger.h" + +#include "core/posix/signal.h" + +anbox::cmds::StartContainer::StartContainer() + : CommandWithFlagsAndAction{cli::Name{"start-container"}, cli::Usage{"start-container"}, cli::Description{"Start a container"}} +{ + action([](const cli::Command::Context& ctxt) { + auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, + core::posix::Signal::sig_int}); + trap->signal_raised().connect([trap](const core::posix::Signal &signal) { + INFO("Signal %i received. Good night.", static_cast(signal)); + trap->stop(); + }); + + auto rt = Runtime::create(); + + rt->start(); + + auto container = std::make_shared(); + container->start(); + + trap->run(); + container->stop(); + rt->stop(); + + return 0; + }); +} diff --git a/src/anbox/cmds/shell.h b/src/anbox/cmds/start_container.h similarity index 84% rename from src/anbox/cmds/shell.h rename to src/anbox/cmds/start_container.h index 4a8d4f3..1d7e107 100644 --- a/src/anbox/cmds/shell.h +++ b/src/anbox/cmds/start_container.h @@ -15,8 +15,8 @@ * */ -#ifndef ANBOX_CMDS_SHELL_H_ -#define ANBOX_CMDS_SHELL_H_ +#ifndef ANBOX_CMDS_START_CONTAINER_H_ +#define ANBOX_CMDS_START_CONTAINER_H_ #include #include @@ -26,12 +26,9 @@ namespace anbox { namespace cmds { -class Shell : public cli::CommandWithFlagsAndAction { +class StartContainer : public cli::CommandWithFlagsAndAction { public: - Shell(); - -private: - int pid_; + StartContainer(); }; } // namespace cmds } // namespace anbox diff --git a/src/anbox/config.cpp b/src/anbox/config.cpp index 18bf49e..6c696ff 100644 --- a/src/anbox/config.cpp +++ b/src/anbox/config.cpp @@ -24,48 +24,113 @@ namespace fs = boost::filesystem; +namespace { +std::string prefix_dir_from_env(const std::string &path, const std::string &env_var) { + static auto snap_data_path = anbox::utils::get_env_value(env_var, ""); + auto result = path; + if (!snap_data_path.empty()) + result = anbox::utils::string_format("%s/%s", snap_data_path, path); + return result; +} +} + namespace anbox { namespace config { -std::string data_path() { +std::string in_snap_dir(const std::string &path) { + return prefix_dir_from_env(path, "SNAP"); +} + +std::string in_snap_data_dir(const std::string &path) { + return prefix_dir_from_env(path, "SNAP_DATA"); +} + +std::string in_snap_user_data_dir(const std::string &path) { + return prefix_dir_from_env(path, "SNAP_USER_DATA"); +} + +std::string home_path() { static std::string path; - if (path.length() == 0) { - const auto home_path = utils::get_env_value("HOME", "/home/phablet"); - path = utils::string_format("%s/.local/share/anbox", home_path); - fs::create_directories(fs::path(path)); + if (path.empty()) { + path = utils::get_env_value("HOME", ""); + if (path.empty()) + BOOST_THROW_EXCEPTION(std::runtime_error("No home directory specified")); } return path; } -std::string host_share_path() { +std::string runtime_dir() { static std::string path; - if (path.length() == 0) - path = utils::string_format("%s/share", data_path()); + if (path.empty()) { + path = utils::get_env_value("XDG_RUNTIME_DIR", ""); + if (path.empty()) + BOOST_THROW_EXCEPTION(std::runtime_error("No runtime directory specified")); + } return path; } -std::string container_share_path() { - return "/data/anbox-share"; +std::string state_dir() { + static std::string path = "/var/lib"; + return path; +} + +std::string log_path() { + static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/", state_dir())); + return path; +} + +std::string socket_path() { + + static std::string path = utils::string_format("%s/anbox/sockets", runtime_dir()); + return path; +} + +std::string data_path() { + static std::string path = utils::string_format("%s/.local/share/anbox/data", home_path()); + return path; +} + +std::string rootfs_path() { + static std::string path = in_snap_dir(utils::string_format("%s/anbox/rootfs", state_dir())); + return path; +} + +std::string container_config_path() { + static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/containers", state_dir())); + return path; +} + +std::string container_socket_path() { + std::string path = "/run/anbox-container.socket"; + return path; +} + +std::string host_share_path() { + static std::string path = utils::string_format("%s/android-share", data_path()); + return path; +} + +std::string host_input_device_path() { + static std::string path = utils::string_format("%s/input-devices", data_path()); + return path; } std::string host_android_data_path() { - static std::string path; - if (path.length() == 0) - path = utils::string_format("%s/android-data", data_path()); + static std::string path = utils::string_format("%s/android-data", data_path()); return path; } std::string host_android_cache_path() { - static std::string path; - if (path.length() == 0) - path = utils::string_format("%s/android-cache", data_path()); + static std::string path = utils::string_format("%s/android-cache", data_path()); return path; } std::string host_android_storage_path() { - static std::string path; - if (path.length() == 0) - path = utils::string_format("%s/android-storage", data_path()); + static std::string path = utils::string_format("%s/android-storage", data_path()); return path; } + +std::string container_android_share_path() { + return "/data/anbox-share"; +} } // namespace config } // namespace anbox diff --git a/src/anbox/config.h b/src/anbox/config.h index 5de04f5..6b6c496 100644 --- a/src/anbox/config.h +++ b/src/anbox/config.h @@ -22,12 +22,21 @@ namespace anbox { namespace config { +std::string in_snap_dir(const std::string &path); +std::string in_snap_data_dir(const std::string &path); +std::string in_snap_user_data_dir(const std::string &path); std::string data_path(); +std::string rootfs_path(); +std::string log_path(); +std::string socket_path(); +std::string container_config_path(); +std::string container_socket_path(); std::string host_share_path(); -std::string container_share_path(); +std::string host_input_device_path(); std::string host_android_data_path(); std::string host_android_cache_path(); std::string host_android_storage_path(); +std::string container_android_share_path(); } // namespace config } // namespace anbox diff --git a/src/anbox/container.cpp b/src/anbox/container.cpp deleted file mode 100644 index f41add2..0000000 --- a/src/anbox/container.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2016 Simon Fels - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - */ - -#include - -#include - -#include -#include - -#include "core/posix/fork.h" -#include "core/posix/exec.h" - -#include "anbox/utils.h" -#include "anbox/logger.h" -#include "anbox/config.h" -#include "anbox/container.h" -#include "anbox/common/fd.h" - -#include -#include - -namespace fs = boost::filesystem; -namespace anbox { - -Container::Spec Container::Spec::Default() { - Spec spec; - spec.init_command = "/anbox-init.sh"; - spec.environment.insert({"PATH", "/system/bin:/system/sbin:/system/xbin"}); - return spec; -} -std::shared_ptr Container::create(const Container::Spec &spec) { - return std::shared_ptr(new Container{spec}); -} - -Container::Container(const Container::Spec &spec) : - spec_(spec), - child_(core::posix::ChildProcess::invalid()), - child_group_(core::posix::ProcessGroup::invalid()) { -} - -Container::~Container() { - stop(); -} - -void Container::start() { - std::vector args = { - // We need to setup user mapping here as lxc-usernsexec will not - // map our current user to root which we need to allow our container - // to access files we've created and mapped into it - "-m", utils::string_format("u:0:%d:1", getuid()), - "-m", utils::string_format("g:0:%d:1", getgid()), - // For all other users inside the container we're using a subuid/ - // subgid range which is defined on the host. - "-m", "u:1:100000:65536", - "-m", "g:1:100000:65536", - "--", - utils::string_format("%s/usr/bin/anbox-container", utils::get_env_value("SNAP", "")), - "--bind", spec_.rootfs_path, "/", - "--dev", "/dev", - "--proc", "/proc", - "--unshare-ipc", - "--unshare-pid", - "--unshare-uts", - "--chdir", "/", - "--pid-file", utils::string_format("%s/pid", config::data_path()), - }; - - for (const auto &dir : spec_.temporary_dirs) { - args.push_back("--tmpfs"); - args.push_back(dir); - } - - for (const auto &path : spec_.dev_bind_paths) { - args.push_back("--dev-bind"); - args.push_back(path); - args.push_back(path); - } - - for (const auto &path : spec_.bind_paths) { - args.push_back("--bind"); - args.push_back(path.first); - args.push_back(path.second); - } - - for (const auto &env : spec_.environment) { - args.push_back("--setenv"); - args.push_back(env.first); - args.push_back(env.second); - } - - args.push_back(spec_.init_command); - - std::map env = { - // lxc-usernsexec needs this as otherwise it doesn't find the - // newuidmap/newgidmap utilities it uses to setup the user - // namespace - { "PATH", "/usr/bin" }, - }; - - child_ = core::posix::exec( - utils::string_format("%s/usr/bin/lxc-usernsexec", utils::get_env_value("SNAP", "")), - args, env, core::posix::StandardStream::empty); - child_group_ = child_.process_group_or_throw(); -} - -void Container::stop() { - child_group_.send_signal_or_throw(core::posix::Signal::sig_kill); -} - -} // namespace anbox diff --git a/src/anbox/container.h b/src/anbox/container.h deleted file mode 100644 index 8e12134..0000000 --- a/src/anbox/container.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 Simon Fels - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - */ - -#ifndef ANBOX_CONTAINER_H_ -#define ANBOX_CONTAINER_H_ - -#include -#include -#include -#include - -#include "core/posix/child_process.h" - -#include "anbox/do_not_copy_or_move.h" -#include "anbox/runtime.h" - -#include - -namespace anbox { -class Container : public DoNotCopyOrMove, - public std::enable_shared_from_this { -public: - class Spec { - public: - static Spec Default(); - - std::string rootfs_path; - std::string init_command; - std::vector temporary_dirs; - std::map bind_paths; - std::vector dev_bind_paths; - std::map environment; - }; - - static std::shared_ptr create(const Spec &spec); - - virtual ~Container(); - - void start(); - void stop(); - -private: - Container(const Container::Spec &spec); - - Spec spec_; - core::posix::ChildProcess child_; - core::posix::ProcessGroup child_group_; -}; -} // namespace anbox - -#endif diff --git a/src/anbox/cmds/shell.cpp b/src/anbox/container/client.cpp similarity index 54% rename from src/anbox/cmds/shell.cpp rename to src/anbox/container/client.cpp index 20fd33d..f6d5d51 100644 --- a/src/anbox/cmds/shell.cpp +++ b/src/anbox/container/client.cpp @@ -15,24 +15,17 @@ * */ -#include - -#include "core/posix/signal.h" - -#include "anbox/logger.h" +#include "anbox/container/client.h" #include "anbox/config.h" -#include "anbox/container_connector.h" -#include "anbox/cmds/shell.h" +#include "anbox/logger.h" -namespace fs = boost::filesystem; - -anbox::cmds::Shell::Shell() - : CommandWithFlagsAndAction{cli::Name{"shell"}, cli::Usage{"shell"}, cli::Description{"Open a shell within the Anbox container"}}, - pid_(-1) -{ - flag(cli::make_flag(cli::Name{"pid"}, cli::Description{"PID of container to attach to"}, pid_)); - action([this](const cli::Command::Context &) { - ContainerConnector connector(pid_); - return connector.run("/system/bin/sh"); - }); +namespace anbox { +namespace container { +Client::Client(const std::shared_ptr &rt) : + messenger_(config::container_socket_path(), rt) { } + +Client::~Client() { +} +} // namespace container +} // namespace anbox diff --git a/src/anbox/container/client.h b/src/anbox/container/client.h new file mode 100644 index 0000000..e30c630 --- /dev/null +++ b/src/anbox/container/client.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_CLIENT_H_ +#define ANBOX_CONTAINER_CLIENT_H_ + +#include "anbox/network/local_socket_messenger.h" +#include "anbox/runtime.h" + +namespace anbox { +namespace container { +class Client { +public: + Client(const std::shared_ptr &rt); + ~Client(); + +private: + network::LocalSocketMessenger messenger_; +}; +} // namespace container +} // namespace anbox + +#endif diff --git a/src/anbox/container/container.cpp b/src/anbox/container/container.cpp new file mode 100644 index 0000000..b25f23f --- /dev/null +++ b/src/anbox/container/container.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/container/container.h" + +namespace anbox { +namespace container { +Container::~Container() { +} +} // namespace container +} // namespace anbox diff --git a/src/anbox/container/container.h b/src/anbox/container/container.h new file mode 100644 index 0000000..2712090 --- /dev/null +++ b/src/anbox/container/container.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_CONTAINER_H_ +#define ANBOX_CONTAINER_CONTAINER_H_ + +#include +#include + +namespace anbox { +namespace container { +class Container { +public: + virtual ~Container(); + + enum class State { + inactive, + running, + }; + + // Start the container in background + virtual void start() = 0; + + // Stop a running container + virtual void stop() = 0; + + // Get the current container state + virtual State state() = 0; +}; +} // namespace container +} // namespace anbox + +#endif diff --git a/src/anbox/container/lxc_container.cpp b/src/anbox/container/lxc_container.cpp new file mode 100644 index 0000000..cd04636 --- /dev/null +++ b/src/anbox/container/lxc_container.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/container/lxc_container.h" +#include "anbox/utils.h" +#include "anbox/config.h" +#include "anbox/logger.h" + +#include +#include + +#include + +#include +#include +#include +#include + +namespace { +static constexpr char *kAndroidShellCommand{"/system/bin/ls"}; +} + +namespace anbox { +namespace container { +LxcContainer::LxcContainer() : + state_(State::inactive), + container_(nullptr) { + utils::ensure_paths({ + config::container_config_path(), + config::log_path(), + config::host_share_path(), + config::host_android_data_path(), + config::host_android_cache_path(), + config::host_android_storage_path(), + config::host_input_device_path(), + }); +} + +LxcContainer::~LxcContainer() { + if (container_) + lxc_container_put(container_); +} + +void LxcContainer::start() { + if (getuid() != 0) + BOOST_THROW_EXCEPTION(std::runtime_error("You have to start the container as root")); + + if (container_ && container_->is_running(container_)) + BOOST_THROW_EXCEPTION(std::runtime_error("Container already started")); + + if (!container_) { + DEBUG("Containers are stored in %s", config::container_config_path()); + + // Remove container config to be be able to rewrite it + ::unlink(utils::string_format("%s/default/config", config::container_config_path()).c_str()); + + container_ = lxc_container_new("default", config::container_config_path().c_str()); + if (!container_) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create LXC container instance")); + } + + // We drop all not needed capabilities + set_config_item("lxc.cap.drop", "mac_admin mac_override sys_time sys_module sys_rawio"); + + // We can mount proc/sys as rw here as we will run the container unprivileged in the end + set_config_item("lxc.mount.auto", "proc:mixed sys:mixed cgroup:mixed"); + + set_config_item("lxc.autodev", "1"); + set_config_item("lxc.pts", "1024"); + set_config_item("lxc.tty", "0"); + set_config_item("lxc.utsname", "anbox"); + + set_config_item("lxc.group.devices.deny",""); + set_config_item("lxc.group.devices.allow",""); + + // We can't move bind-mounts, so don't use /dev/lxc/ + set_config_item("lxc.devttydir", ""); + + set_config_item("lxc.environment", "PATH=/system/bin:/system/sbin:/system/xbin"); + + set_config_item("lxc.init_cmd", "/anbox-init.sh"); + set_config_item("lxc.rootfs.backend", "dir"); + + DEBUG("Using rootfs path %s", config::rootfs_path()); + set_config_item("lxc.rootfs", config::rootfs_path()); + + set_config_item("lxc.loglevel", "0"); + set_config_item("lxc.logfile", utils::string_format("%s/container.log", config::log_path()).c_str()); + +#if 0 + // Android uses namespaces as well so we have to allow nested namespaces for LXC + // which are otherwise forbidden by AppArmor. + set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting"); +#else + // FIXME: when using the nested profile we still get various denials from things + // Android tries to do but isn't allowed to. We need to look into those and see + // how we can switch back to a confined way of running the container. + set_config_item("lxc.aa_profile", "unconfined"); +#endif + + std::map bind_mount_dirs = { + { config::host_share_path(), config::container_android_share_path() }, + { config::host_android_data_path(), "data" }, + { config::host_android_cache_path(), "cache" }, + { config::host_android_storage_path(), "storage" }, + { config::host_input_device_path(), "dev/input" }, + }; + + for (const auto &item : bind_mount_dirs) { + set_config_item("lxc.mount.entry", + utils::string_format("%s %s none bind,create=dir,optional 0 0", item.first, item.second)); + } + + std::map bind_mount_files = { + { "/dev/binder", "dev/binder" }, + { "/dev/ashmem", "dev/ashmem" }, + { utils::string_format("%s/qemu_pipe", config::socket_path()), "dev/qemu_pipe" }, + { utils::string_format("%s/qemud", config::socket_path()), "dev/qemud" }, + { utils::string_format("%s/anbox_bridge", config::socket_path()), "dev/anbox_bridge" }, + }; + + for (const auto &item : bind_mount_files) { + set_config_item("lxc.mount.entry", + utils::string_format("%s %s none bind,create=file,optional 0 0", item.first, item.second)); + } + + if (!container_->save_config(container_, nullptr)) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to save container configuration")); + + if (not container_->start(container_, 0, nullptr)) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start container")); + + DEBUG("Container successfully started"); +} + +void LxcContainer::stop() { + if (not container_ || not container_->is_running(container_)) + BOOST_THROW_EXCEPTION(std::runtime_error("Cannot stop container as it is not running")); + + if (not container_->stop(container_)) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to stop container")); + + DEBUG("Container successfully stopped"); +} + +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")); +} + +Container::State LxcContainer::state() { + return state_; +} +} // namespace container +} // namespace anbox diff --git a/src/anbox/container/lxc_container.h b/src/anbox/container/lxc_container.h new file mode 100644 index 0000000..ad8cabb --- /dev/null +++ b/src/anbox/container/lxc_container.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_LXC_CONTAINER_H_ +#define ANBOX_CONTAINER_LXC_CONTAINER_H_ + +#include "anbox/container/container.h" + +#include + +#include + +namespace anbox { +namespace container { +class LxcContainer : public Container { +public: + LxcContainer(); + ~LxcContainer(); + + void start() override; + void stop() override; + State state() override; + +private: + void set_config_item(const std::string &key, const std::string &value); + + State state_; + lxc_container *container_; +}; +} // namespace container +} // namespace anbox + +#endif diff --git a/src/anbox/container/service.cpp b/src/anbox/container/service.cpp new file mode 100644 index 0000000..5d5f5ce --- /dev/null +++ b/src/anbox/container/service.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/container/service.h" +#include "anbox/network/delegate_connection_creator.h" +#include "anbox/network/delegate_message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/qemu/null_message_processor.h" +#include "anbox/container/lxc_container.h" +#include "anbox/config.h" +#include "anbox/logger.h" + +namespace anbox { +namespace container { +std::shared_ptr Service::create(const std::shared_ptr &rt) { + auto sp = std::make_shared(rt); + + auto delegate_connector = std::make_shared( + [sp](std::shared_ptr const &socket) { + sp->new_client(socket); + }); + + sp->connector_ = std::make_shared( + config::container_socket_path(), rt, delegate_connector); + + // Make sure others can connect to our socket + ::chmod(config::container_socket_path().c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + sp->connections_->set_observer(sp); + + DEBUG("Everything setup. Waiting for incoming connections."); + + return sp; +} + +Service::Service(const std::shared_ptr &rt) : + dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)), + next_connection_id_(0), + connections_(std::make_shared>()) { +} + +Service::~Service() { +} + +void Service::connection_added(int id) { +} + +void Service::connection_removed(int id) { + stop_container(); +} + +void Service::start_container() { + if (backend_) + BOOST_THROW_EXCEPTION(std::runtime_error("Container is already running")); + + backend_ = std::make_shared(); + backend_->start(); +} + +void Service::stop_container() { + if (not backend_) + return; + + backend_->stop(); + backend_.reset(); +} + +int Service::next_id() { + return next_connection_id_++; +} + +void Service::new_client(std::shared_ptr const &socket) { + if (connections_->size() >= 1) { + socket->close(); + return; + } + + auto const messenger = std::make_shared(socket); + auto const& connection = std::make_shared( + messenger, messenger, next_id(), connections_, + std::make_shared()); + + connections_->add(connection); + connection->read_next_message(); + + // To get access to the sockets the application manager + // services creates we need to make sure we're running + // against the right runtime directory here. Its a bit + // hacky but works for now. + auto creds = messenger->creds(); + setenv("XDG_RUNTIME_DIR", utils::string_format("/run/user/%d", creds.uid()).c_str(), 1); + + DEBUG("Got connection from pid %d", creds.pid()); + + start_container(); +} +} // namespace container +} // namespace anbox diff --git a/src/anbox/container/service.h b/src/anbox/container/service.h new file mode 100644 index 0000000..b717de4 --- /dev/null +++ b/src/anbox/container/service.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_SERVICE_H_ +#define ANBOX_CONTAINER_SERVICE_H_ + +#include "anbox/network/published_socket_connector.h" +#include "anbox/network/connections.h" +#include "anbox/network/socket_connection.h" +#include "anbox/network/credentials.h" +#include "anbox/container/container.h" +#include "anbox/common/dispatcher.h" +#include "anbox/runtime.h" + +namespace anbox { +namespace container { +class Service : public network::Connections::Observer, + public std::enable_shared_from_this { +public: + static std::shared_ptr create(const std::shared_ptr &rt); + + Service(const std::shared_ptr &rt); + ~Service(); + + void connection_added(int id) override; + void connection_removed(int id) override; + +private: + int next_id(); + void new_client(std::shared_ptr const &socket); + + void start_container(); + void stop_container(); + + std::shared_ptr dispatcher_; + std::shared_ptr connector_; + std::atomic next_connection_id_; + std::shared_ptr> const connections_; + std::shared_ptr backend_; +}; +} // namespace container +} // namespace anbox + +#endif diff --git a/src/anbox/container_connector.cpp b/src/anbox/container_connector.cpp deleted file mode 100644 index d6ecd82..0000000 --- a/src/anbox/container_connector.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2016 Simon Fels - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - */ - -#include "anbox/container_connector.h" -#include "anbox/namespace_attacher.h" -#include "anbox/config.h" -#include "anbox/utils.h" -#include "anbox/logger.h" - -#include "core/posix/exec.h" - -#include - -#include - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace anbox { -ContainerConnector::ContainerConnector(int pid) : - pid_(pid) { -} - -ContainerConnector::~ContainerConnector() { -} - -int ContainerConnector::run(const std::string &path) { - int pid = pid_; - if (pid == -1) - pid = std::stol(utils::read_file_if_exists_or_throw( - utils::string_format("%s/pid", config::data_path()))); - - if (!fs::is_directory(fs::path(utils::string_format("/proc/%i", pid)))) - BOOST_THROW_EXCEPTION(std::runtime_error("Container isn't running")); - - namespaces_ = std::make_shared(std::vector{ - NamespaceType::user, - NamespaceType::mount, - NamespaceType::pid, - NamespaceType::uts, - NamespaceType::ipc, - }, pid); - - // A few things we want to preset in our env within the container shell - std::map env = { - { "ANDROID_ROOT", "/system" }, - { "ANDROID_DATA", "/data" }, - }; - - auto child = core::posix::exec(path, {}, env, core::posix::StandardStream::empty, [this]() { - // We're now in the namespace bwrap spawns up for the container which has - // a /newroot directory pointing to the new root filesystem we get from - // Android and is what we need to change to in order to work within - // the Anroid system. - if (::chroot("/newroot") != 0) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to enter container root filesystem")); - - setuid(0); - setgid(0); - setgroups(0, nullptr); - - chdir("/"); - }); - - child.wait_for(core::posix::wait::Flags::untraced); - - return EXIT_SUCCESS; -} - -} // namespace diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp index f43e0a2..1b553ce 100644 --- a/src/anbox/daemon.cpp +++ b/src/anbox/daemon.cpp @@ -24,10 +24,11 @@ #include "anbox/cmds/version.h" #include "anbox/cmds/run.h" -#include "anbox/cmds/shell.h" #include "anbox/cmds/install_app.h" #include "anbox/cmds/launch_app.h" #include "anbox/cmds/reset.h" +#include "anbox/cmds/container_manager.h" +#include "anbox/cmds/start_container.h" #include @@ -39,34 +40,19 @@ Daemon::Daemon() : cmd.command(std::make_shared()) .command(std::make_shared()) - .command(std::make_shared()) .command(std::make_shared()) .command(std::make_shared()) - .command(std::make_shared()); + .command(std::make_shared()) + .command(std::make_shared()) + .command(std::make_shared()); } int Daemon::Run(const std::vector &arguments) try { - ensure_data_path(); return cmd.run({std::cin, std::cout, arguments}); } catch(std::exception &err) { ERROR("%s", err.what()); return EXIT_FAILURE; } - -void Daemon::ensure_data_path() { - std::vector paths = { - config::data_path(), - config::host_share_path(), - config::host_android_data_path(), - config::host_android_cache_path(), - config::host_android_storage_path() - }; - - for (const auto &path: paths) { - if (!fs::is_directory(fs::path(path))) - fs::create_directories(fs::path(path)); - } -} } // namespace anbox diff --git a/src/anbox/daemon.h b/src/anbox/daemon.h index 5f9a14f..cba8ca2 100644 --- a/src/anbox/daemon.h +++ b/src/anbox/daemon.h @@ -32,8 +32,6 @@ public: int Run(const std::vector &arguments); private: - void ensure_data_path(); - cli::CommandWithSubcommands cmd; }; } // namespace anbox diff --git a/src/anbox/input/device.cpp b/src/anbox/input/device.cpp index 6289fd1..a0b2958 100644 --- a/src/anbox/input/device.cpp +++ b/src/anbox/input/device.cpp @@ -154,8 +154,7 @@ std::string Device::socket_path() const { return connector_->socket_file(); } -int Device::next_id() -{ +int Device::next_id() { return next_connection_id_++; } diff --git a/src/anbox/input/manager.cpp b/src/anbox/input/manager.cpp index d4238f4..95eb15b 100644 --- a/src/anbox/input/manager.cpp +++ b/src/anbox/input/manager.cpp @@ -39,22 +39,13 @@ std::shared_ptr Manager::create_device() { return device; } -void Manager::generate_mappings(std::map &target) { - for (const auto &iter : devices_) { - target.insert({ - iter.second->socket_path(), - (boost::format("/dev/input/event%1%") % iter.first).str(), - }); - } -} - std::uint32_t Manager::next_id() { static std::uint32_t next_id = 0; return next_id++; } std::string Manager::build_device_path(const std::uint32_t &id) { - return (boost::format("%1%/input_device_%2%") % config::data_path() % id).str(); + return (boost::format("%1%/event%2%") % config::host_input_device_path() % id).str(); } } // namespace input diff --git a/src/anbox/input/manager.h b/src/anbox/input/manager.h index 5920368..f3a5c38 100644 --- a/src/anbox/input/manager.h +++ b/src/anbox/input/manager.h @@ -32,8 +32,6 @@ public: std::shared_ptr create_device(); - void generate_mappings(std::map &target); - private: std::uint32_t next_id(); std::string build_device_path(const std::uint32_t &id); diff --git a/src/anbox/network/connections.h b/src/anbox/network/connections.h index dcc9618..1bbf2ca 100644 --- a/src/anbox/network/connections.h +++ b/src/anbox/network/connections.h @@ -29,19 +29,34 @@ template class Connections { public: + class Observer { + public: + virtual void connection_added(int id) = 0; + virtual void connection_removed(int id) = 0; + }; + Connections() {} ~Connections() { clear(); } + void set_observer(const std::shared_ptr &o) + { + observer = o; + } + void add(std::shared_ptr const& connection) { std::unique_lock lock(mutex); connections.insert({connection->id(), connection}); + if (observer) + observer->connection_added(connection->id()); } void remove(int id) { std::unique_lock lock(mutex); connections.erase(id); + if (observer) + observer->connection_removed(id); } bool includes(int id) const @@ -72,6 +87,7 @@ private: std::mutex mutex; std::map> connections; + std::shared_ptr observer; }; } // namespace anbox } // namespace network diff --git a/src/anbox/network/credentials.cpp b/src/anbox/network/credentials.cpp new file mode 100644 index 0000000..91a943b --- /dev/null +++ b/src/anbox/network/credentials.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/network/credentials.h" + +namespace anbox { +namespace network { +Credentials::Credentials(pid_t pid, uid_t uid, gid_t gid) : + pid_{pid}, + uid_{uid}, + gid_{gid} { +} + +pid_t Credentials::pid() const { + return pid_; +} + +uid_t Credentials::uid() const { + return uid_; +} + +gid_t Credentials::gid() const { + return gid_; +} +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/credentials.h b/src/anbox/network/credentials.h new file mode 100644 index 0000000..f97abd7 --- /dev/null +++ b/src/anbox/network/credentials.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_CREDENTIALS_H_ +#define ANBOX_NETWORK_CREDENTIALS_H_ + +#include + +namespace anbox { +namespace network { +class Credentials { +public: + Credentials(pid_t pid, uid_t uid, gid_t gid); + + pid_t pid() const; + uid_t uid() const; + gid_t gid() const; + +private: + Credentials() = delete; + + pid_t pid_; + uid_t uid_; + gid_t gid_; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/local_socket_messenger.cpp b/src/anbox/network/local_socket_messenger.cpp new file mode 100644 index 0000000..24d101d --- /dev/null +++ b/src/anbox/network/local_socket_messenger.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/network/local_socket_messenger.h" +#include "anbox/network/socket_helper.h" +#include "anbox/utils.h" + +#include + +namespace anbox { +namespace network { +LocalSocketMessenger::LocalSocketMessenger(const std::string &path, + const std::shared_ptr &rt) : + socket_(std::make_shared(rt->service())) { + + boost::system::error_code err; + socket_->connect(boost::asio::local::stream_protocol::endpoint(path), err); + if (err) { + const auto msg = utils::string_format("Failed to connect to socket %s: %s", path, err.message()); + BOOST_THROW_EXCEPTION(std::runtime_error(msg)); + } + + messenger_ = std::make_shared(socket_); +} + +LocalSocketMessenger::~LocalSocketMessenger() { +} + +void LocalSocketMessenger::send(char const* data, size_t length) { + messenger_->send(data, length); +} + +void LocalSocketMessenger::async_receive_msg(AnboxReadHandler const& handle, boost::asio::mutable_buffers_1 const &buffer) { + messenger_->async_receive_msg(handle, buffer); +} + +boost::system::error_code LocalSocketMessenger::receive_msg(boost::asio::mutable_buffers_1 const& buffer) { + return messenger_->receive_msg(buffer); +} + +size_t LocalSocketMessenger::available_bytes() { + return messenger_->available_bytes(); +} +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/local_socket_messenger.h b/src/anbox/network/local_socket_messenger.h new file mode 100644 index 0000000..a9c963f --- /dev/null +++ b/src/anbox/network/local_socket_messenger.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_LOCAL_SOCKET_MESSENGER_H_ +#define ANBOX_NETWORK_LOCAL_SOCKET_MESSENGER_H_ + +#include "anbox/network/socket_messenger.h" +#include "anbox/network/message_sender.h" +#include "anbox/network/message_receiver.h" +#include "anbox/runtime.h" + +#include + +namespace anbox { +namespace network { +class LocalSocketMessenger : public MessageSender, + public MessageReceiver { +public: + LocalSocketMessenger(const std::string &path, const std::shared_ptr &rt); + ~LocalSocketMessenger(); + + void send(char const* data, size_t length) override; + void async_receive_msg(AnboxReadHandler const& handle, boost::asio::mutable_buffers_1 const &buffer) override; + boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) override; + size_t available_bytes() override; + +private: + std::shared_ptr socket_; + std::shared_ptr messenger_; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/published_socket_connector.cpp b/src/anbox/network/published_socket_connector.cpp index aa0c4e7..94e2b14 100644 --- a/src/anbox/network/published_socket_connector.cpp +++ b/src/anbox/network/published_socket_connector.cpp @@ -15,73 +15,12 @@ * */ -#include - -#include -#include -#include - -#include - #include "anbox/network/published_socket_connector.h" #include "anbox/network/connection_context.h" +#include "anbox/network/socket_helper.h" namespace { -bool socket_file_exists(std::string const& filename) -{ - struct stat statbuf; - bool exists = (0 == stat(filename.c_str(), &statbuf)); - /* Avoid removing non-socket files */ - bool is_socket_type = (statbuf.st_mode & S_IFMT) == S_IFSOCK; - return exists && is_socket_type; -} -bool socket_exists(std::string const& socket_name) -{ - try - { - std::string socket_path{socket_name}; - - /* In case an abstract socket name exists with the same name*/ - socket_path.insert(std::begin(socket_path), ' '); - - /* If the name is contained in this table, it signifies - * a process is truly using that socket connection - */ - std::ifstream socket_names_file("/proc/net/unix"); - std::string line; - while (std::getline(socket_names_file, line)) - { - auto index = line.find(socket_path); - /* check for complete match */ - if (index != std::string::npos && - (index + socket_path.length()) == line.length()) - { - return true; - } - } - } - catch (...) - { - /* Assume the socket exists */ - return true; - } - return false; -} - -std::string remove_if_stale(std::string const& socket_name) -{ - if (socket_file_exists(socket_name) && !socket_exists(socket_name)) - { - if (std::remove(socket_name.c_str()) != 0) - { - BOOST_THROW_EXCEPTION( - boost::enable_error_info( - std::runtime_error("Failed removing stale socket file")) << boost::errinfo_errno(errno)); - } - } - return socket_name; -} } namespace anbox { @@ -90,7 +29,7 @@ PublishedSocketConnector::PublishedSocketConnector( const std::string& socket_file, const std::shared_ptr &rt, const std::shared_ptr &connection_creator) : - socket_file_(remove_if_stale(socket_file)), + socket_file_(remove_socket_if_stale(socket_file)), runtime_(rt), connection_creator_(connection_creator), acceptor_(rt->service(), socket_file_) { diff --git a/src/anbox/network/socket_helper.cpp b/src/anbox/network/socket_helper.cpp new file mode 100644 index 0000000..8db0447 --- /dev/null +++ b/src/anbox/network/socket_helper.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/network/socket_helper.h" + +#include + +#include +#include +#include + +#include + +namespace anbox { +namespace network { +bool socket_file_exists(std::string const& filename) +{ + struct stat statbuf; + bool exists = (0 == stat(filename.c_str(), &statbuf)); + /* Avoid removing non-socket files */ + bool is_socket_type = (statbuf.st_mode & S_IFMT) == S_IFSOCK; + return exists && is_socket_type; +} + +bool socket_exists(std::string const& socket_name) +{ + try + { + std::string socket_path{socket_name}; + + /* In case an abstract socket name exists with the same name*/ + socket_path.insert(std::begin(socket_path), ' '); + + /* If the name is contained in this table, it signifies + * a process is truly using that socket connection + */ + std::ifstream socket_names_file("/proc/net/unix"); + std::string line; + while (std::getline(socket_names_file, line)) + { + auto index = line.find(socket_path); + /* check for complete match */ + if (index != std::string::npos && + (index + socket_path.length()) == line.length()) + { + return true; + } + } + } + catch (...) + { + /* Assume the socket exists */ + return true; + } + return false; +} + +std::string remove_socket_if_stale(std::string const& socket_name) +{ + if (socket_file_exists(socket_name) && !socket_exists(socket_name)) + { + if (std::remove(socket_name.c_str()) != 0) + { + BOOST_THROW_EXCEPTION( + boost::enable_error_info( + std::runtime_error("Failed removing stale socket file")) << boost::errinfo_errno(errno)); + } + } + return socket_name; +} +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/socket_helper.h b/src/anbox/network/socket_helper.h new file mode 100644 index 0000000..c3e9b11 --- /dev/null +++ b/src/anbox/network/socket_helper.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_SOCKET_HELPER_H_ +#define ANBOX_NETWORK_SOCKET_HELPER_H_ + +#include + +namespace anbox { +namespace network { +bool socket_file_exists(std::string const& filename); +bool socket_exists(std::string const& socket_name); +std::string remove_socket_if_stale(std::string const& socket_name); +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/socket_messenger.cpp b/src/anbox/network/socket_messenger.cpp index ea27921..5ab9673 100644 --- a/src/anbox/network/socket_messenger.cpp +++ b/src/anbox/network/socket_messenger.cpp @@ -46,6 +46,18 @@ SocketMessenger::SocketMessenger(std::shared_ptrset_option(option); } +Credentials SocketMessenger::creds() const { + struct ucred cr; + socklen_t cl = sizeof(cr); + + auto status = getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl); + + if (status) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials")); + + return {cr.pid, cr.uid, cr.gid}; +} + void SocketMessenger::send(char const* data, size_t length) { VariableLengthArray whole_message{length}; diff --git a/src/anbox/network/socket_messenger.h b/src/anbox/network/socket_messenger.h index cb73a10..b42f96f 100644 --- a/src/anbox/network/socket_messenger.h +++ b/src/anbox/network/socket_messenger.h @@ -24,6 +24,7 @@ #include "anbox/common/fd_sets.h" #include "anbox/network/message_sender.h" #include "anbox/network/message_receiver.h" +#include "anbox/network/credentials.h" namespace anbox { namespace network { @@ -32,7 +33,9 @@ class SocketMessenger : public MessageSender, public: SocketMessenger(std::shared_ptr const& socket); - void send(char const* data, size_t length); + Credentials creds() const; + + void send(char const* data, size_t length) override; void async_receive_msg(AnboxReadHandler const& handle, boost::asio::mutable_buffers_1 const &buffer) override; boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) override; diff --git a/src/anbox/qemu/boot_properties_message_processor.cpp b/src/anbox/qemu/boot_properties_message_processor.cpp index d0e8124..d6390ff 100644 --- a/src/anbox/qemu/boot_properties_message_processor.cpp +++ b/src/anbox/qemu/boot_properties_message_processor.cpp @@ -55,7 +55,7 @@ void BootPropertiesMessageProcessor::list_properties() { // TODO(morphis): Using HDPI here for now but should be adjusted to the device // we're running on. - "ro.sf.lcd_density=480", + "ro.sf.lcd_density=200", // libhwui detects that we support certain GLESv3 extensions which // we don't yet support in our host channel so we have to disable diff --git a/src/anbox/ubuntu/window.cpp b/src/anbox/ubuntu/window.cpp index 4aca99f..fd394cf 100644 --- a/src/anbox/ubuntu/window.cpp +++ b/src/anbox/ubuntu/window.cpp @@ -461,7 +461,7 @@ void Window::process_input_event(const SDL_Event &event) { mouse_events.push_back({ EV_SYN, SYN_REPORT, 0 }); break; case SDL_MOUSEWHEEL: - mouse_events.push_back({ EV_REL, REL_WHEEL, static_cast(event.wheel.direction) }); + mouse_events.push_back({ EV_REL, REL_WHEEL, static_cast(event.wheel.y) }); break; case SDL_KEYDOWN: { const auto code = convert_sdl_scancode_to_evdev(event.key.keysym.scancode); diff --git a/src/anbox/ubuntu/window_creator.cpp b/src/anbox/ubuntu/window_creator.cpp index dae1675..ec442c0 100644 --- a/src/anbox/ubuntu/window_creator.cpp +++ b/src/anbox/ubuntu/window_creator.cpp @@ -25,29 +25,34 @@ namespace anbox { namespace ubuntu { WindowCreator::WindowCreator(const std::shared_ptr &input_manager) : graphics::WindowCreator(input_manager), - input_manager_(input_manager) { + input_manager_(input_manager), + event_thread_running_(false) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to initialize SDL")); - event_thread = std::thread(&WindowCreator::process_events, this); + event_thread_ = std::thread(&WindowCreator::process_events, this); } WindowCreator::~WindowCreator() { + event_thread_running_ = false; + event_thread_.join(); } void WindowCreator::process_window_event(const SDL_Event &event) { } void WindowCreator::process_events() { - while(true) { + event_thread_running_ = true; + + while(event_thread_running_) { SDL_Event event; - while (SDL_WaitEvent(&event)) { + while (SDL_WaitEventTimeout(&event, 100)) { switch (event.type) { case SDL_QUIT: - // FIXME once one of our windows is closed we need to decide what - // to do base on the configuration we're running in. - break; + // FIXME: We exit here only as long as we don't have multi window + // support. + BOOST_THROW_EXCEPTION(std::runtime_error("User closed main application window")); case SDL_WINDOWEVENT: process_window_event(event); break; diff --git a/src/anbox/ubuntu/window_creator.h b/src/anbox/ubuntu/window_creator.h index 8664786..a8b6ca0 100644 --- a/src/anbox/ubuntu/window_creator.h +++ b/src/anbox/ubuntu/window_creator.h @@ -48,7 +48,8 @@ private: std::shared_ptr input_manager_; std::map> windows_; std::shared_ptr current_window_; - std::thread event_thread; + std::thread event_thread_; + bool event_thread_running_; }; } // namespace bridge } // namespace anbox diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp index 3a9c1d4..b15d422 100644 --- a/src/anbox/utils.cpp +++ b/src/anbox/utils.cpp @@ -27,6 +27,8 @@ #include "anbox/utils.h" +namespace fs = boost::filesystem; + namespace anbox { namespace utils { @@ -139,5 +141,11 @@ std::string get_env_value(const std::string &name, const std::string &default_va return std::string(value); } +void ensure_paths(const std::vector &paths) { + for (const auto &path: paths) { + if (!fs::is_directory(fs::path(path))) + fs::create_directories(fs::path(path)); + } +} } // namespace utils } // namespace anbox diff --git a/src/anbox/utils.h b/src/anbox/utils.h index 6d6a928..13a0c5c 100644 --- a/src/anbox/utils.h +++ b/src/anbox/utils.h @@ -40,6 +40,8 @@ std::string hex_dump(const uint8_t *data, uint32_t size); std::string get_env_value(const std::string &name, const std::string &default_value = ""); +void ensure_paths(const std::vector &paths); + template static std::string string_format(const std::string& fmt_str, Types&&... args); } // namespace utils diff --git a/src/container_main.cpp b/src/container_main.cpp index d71300f..bbc7bcf 100644 --- a/src/container_main.cpp +++ b/src/container_main.cpp @@ -15,8 +15,12 @@ * */ -extern "C" int bwrap_main(int argc, char **argv); +#include "anbox/container/service.h" int main(int argc, char **argv) { - return bwrap_main(argc, argv); + (void) argc; + (void) argv; + anbox::container::Service service; + + return 0; }