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.
This commit is contained in:
Simon Fels 2016-08-14 15:48:21 +02:00
commit 2b25f6f5bb
44 changed files with 1116 additions and 503 deletions

View file

@ -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

View file

@ -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)

View file

@ -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<Request<protobuf::bridge::Void>>();
protobuf::bridge::InstallApplication message;

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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<int>(signal));
trap->stop();
});
auto rt = Runtime::create();
auto service = container::Service::create(rt);
rt->start();
trap->run();
rt->stop();
return 0;
});
}

View file

@ -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 <functional>
#include <iostream>
#include <memory>
#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<NamespaceAttacher> namespaces_;
ContainerManager();
};
} // namespace
} // namespace cmds
} // namespace anbox
#endif

View file

@ -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 <sys/prctl.h>
@ -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<network::PublishedSocketConnector>(
utils::string_format("%s/qemud", config::data_path()),
utils::string_format("%s/qemud", config::socket_path()),
rt,
std::make_shared<NullConnectionCreator>());
// 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<network::PublishedSocketConnector>(
utils::string_format("%s/qemu_pipe", config::data_path()),
utils::string_format("%s/qemu_pipe", config::socket_path()),
rt,
std::make_shared<qemu::PipeConnectionCreator>(rt,
renderer->socket_path(),
@ -115,7 +113,7 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
auto bridge_connector = std::make_shared<network::PublishedSocketConnector>(
utils::string_format("%s/anbox_bridge", config::data_path()),
utils::string_format("%s/anbox_bridge", config::socket_path()),
rt,
std::make_shared<bridge::ConnectionCreator>(rt,
[&](const std::shared_ptr<network::MessageSender> &sender) {
@ -148,36 +146,8 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
return std::make_shared<bridge::PlatformMessageProcessor>(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;

View file

@ -38,7 +38,6 @@ public:
private:
BusFactory bus_factory_;
std::string rootfs_;
std::string desktop_file_hint_;
std::string apk_;
std::string package_;

View file

@ -0,0 +1,49 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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<int>(signal));
trap->stop();
});
auto rt = Runtime::create();
rt->start();
auto container = std::make_shared<container::LxcContainer>();
container->start();
trap->run();
container->stop();
rt->stop();
return 0;
});
}

View file

@ -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 <functional>
#include <iostream>
@ -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

View file

@ -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

View file

@ -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

View file

@ -1,125 +0,0 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <unistd.h>
#include <boost/filesystem.hpp>
#include <fstream>
#include <sstream>
#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 <grp.h>
#include <sys/mount.h>
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> Container::create(const Container::Spec &spec) {
return std::shared_ptr<Container>(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<std::string> 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<std::string,std::string> 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

View file

@ -1,65 +0,0 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_CONTAINER_H_
#define ANBOX_CONTAINER_H_
#include <string>
#include <vector>
#include <map>
#include <memory.h>
#include "core/posix/child_process.h"
#include "anbox/do_not_copy_or_move.h"
#include "anbox/runtime.h"
#include <sys/types.h>
namespace anbox {
class Container : public DoNotCopyOrMove,
public std::enable_shared_from_this<Container> {
public:
class Spec {
public:
static Spec Default();
std::string rootfs_path;
std::string init_command;
std::vector<std::string> temporary_dirs;
std::map<std::string,std::string> bind_paths;
std::vector<std::string> dev_bind_paths;
std::map<std::string,std::string> environment;
};
static std::shared_ptr<Container> 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

View file

@ -15,24 +15,17 @@
*
*/
#include <boost/filesystem.hpp>
#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<Runtime> &rt) :
messenger_(config::container_socket_path(), rt) {
}
Client::~Client() {
}
} // namespace container
} // namespace anbox

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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<Runtime> &rt);
~Client();
private:
network::LocalSocketMessenger messenger_;
};
} // namespace container
} // namespace anbox
#endif

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/container/container.h"
namespace anbox {
namespace container {
Container::~Container() {
}
} // namespace container
} // namespace anbox

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_CONTAINER_CONTAINER_H_
#define ANBOX_CONTAINER_CONTAINER_H_
#include <string>
#include <vector>
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

View file

@ -0,0 +1,169 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/container/lxc_container.h"
#include "anbox/utils.h"
#include "anbox/config.h"
#include "anbox/logger.h"
#include <stdexcept>
#include <map>
#include <boost/throw_exception.hpp>
#include <sys/wait.h>
#include <sys/capability.h>
#include <sys/types.h>
#include <sys/prctl.h>
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<std::string,std::string> 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<std::string,std::string> 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

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_CONTAINER_LXC_CONTAINER_H_
#define ANBOX_CONTAINER_LXC_CONTAINER_H_
#include "anbox/container/container.h"
#include <string>
#include <lxc/lxccontainer.h>
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

View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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> Service::create(const std::shared_ptr<Runtime> &rt) {
auto sp = std::make_shared<Service>(rt);
auto delegate_connector = std::make_shared<network::DelegateConnectionCreator>(
[sp](std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket) {
sp->new_client(socket);
});
sp->connector_ = std::make_shared<network::PublishedSocketConnector>(
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<Runtime> &rt) :
dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)),
next_connection_id_(0),
connections_(std::make_shared<network::Connections<network::SocketConnection>>()) {
}
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<LxcContainer>();
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<boost::asio::local::stream_protocol::socket> const &socket) {
if (connections_->size() >= 1) {
socket->close();
return;
}
auto const messenger = std::make_shared<network::SocketMessenger>(socket);
auto const& connection = std::make_shared<network::SocketConnection>(
messenger, messenger, next_id(), connections_,
std::make_shared<qemu::NullMessageProcessor>());
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

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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<network::SocketConnection>::Observer,
public std::enable_shared_from_this<Service> {
public:
static std::shared_ptr<Service> create(const std::shared_ptr<Runtime> &rt);
Service(const std::shared_ptr<Runtime> &rt);
~Service();
void connection_added(int id) override;
void connection_removed(int id) override;
private:
int next_id();
void new_client(std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket);
void start_container();
void stop_container();
std::shared_ptr<common::Dispatcher> dispatcher_;
std::shared_ptr<network::PublishedSocketConnector> connector_;
std::atomic<int> next_connection_id_;
std::shared_ptr<network::Connections<network::SocketConnection>> const connections_;
std::shared_ptr<Container> backend_;
};
} // namespace container
} // namespace anbox
#endif

View file

@ -1,88 +0,0 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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 <boost/filesystem.hpp>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <grp.h>
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<NamespaceAttacher>(std::vector<NamespaceType>{
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<std::string, std::string> 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

View file

@ -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 <boost/filesystem.hpp>
@ -39,34 +40,19 @@ Daemon::Daemon() :
cmd.command(std::make_shared<cmds::Version>())
.command(std::make_shared<cmds::Run>())
.command(std::make_shared<cmds::Shell>())
.command(std::make_shared<cmds::InstallApp>())
.command(std::make_shared<cmds::LaunchApp>())
.command(std::make_shared<cmds::Reset>());
.command(std::make_shared<cmds::Reset>())
.command(std::make_shared<cmds::ContainerManager>())
.command(std::make_shared<cmds::StartContainer>());
}
int Daemon::Run(const std::vector<std::string> &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<std::string> 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

View file

@ -32,8 +32,6 @@ public:
int Run(const std::vector<std::string> &arguments);
private:
void ensure_data_path();
cli::CommandWithSubcommands cmd;
};
} // namespace anbox

View file

@ -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_++;
}

View file

@ -39,22 +39,13 @@ std::shared_ptr<Device> Manager::create_device() {
return device;
}
void Manager::generate_mappings(std::map<std::string,std::string> &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

View file

@ -32,8 +32,6 @@ public:
std::shared_ptr<Device> create_device();
void generate_mappings(std::map<std::string,std::string> &target);
private:
std::uint32_t next_id();
std::string build_device_path(const std::uint32_t &id);

View file

@ -29,19 +29,34 @@ template<class Connection>
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<Observer> &o)
{
observer = o;
}
void add(std::shared_ptr<Connection> const& connection)
{
std::unique_lock<std::mutex> lock(mutex);
connections.insert({connection->id(), connection});
if (observer)
observer->connection_added(connection->id());
}
void remove(int id)
{
std::unique_lock<std::mutex> 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<int, std::shared_ptr<Connection>> connections;
std::shared_ptr<Observer> observer;
};
} // namespace anbox
} // namespace network

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_NETWORK_CREDENTIALS_H_
#define ANBOX_NETWORK_CREDENTIALS_H_
#include <sys/types.h>
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

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/network/local_socket_messenger.h"
#include "anbox/network/socket_helper.h"
#include "anbox/utils.h"
#include <boost/system/error_code.hpp>
namespace anbox {
namespace network {
LocalSocketMessenger::LocalSocketMessenger(const std::string &path,
const std::shared_ptr<Runtime> &rt) :
socket_(std::make_shared<boost::asio::local::stream_protocol::socket>(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<SocketMessenger>(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

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#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 <boost/asio/local/stream_protocol.hpp>
namespace anbox {
namespace network {
class LocalSocketMessenger : public MessageSender,
public MessageReceiver {
public:
LocalSocketMessenger(const std::string &path, const std::shared_ptr<Runtime> &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<boost::asio::local::stream_protocol::socket> socket_;
std::shared_ptr<SocketMessenger> messenger_;
};
} // namespace network
} // namespace anbox
#endif

View file

@ -15,73 +15,12 @@
*
*/
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <boost/exception/errinfo_errno.hpp>
#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<Runtime> &rt,
const std::shared_ptr<ConnectionCreator> &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_) {

View file

@ -0,0 +1,86 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/network/socket_helper.h"
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <boost/exception/errinfo_errno.hpp>
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

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_NETWORK_SOCKET_HELPER_H_
#define ANBOX_NETWORK_SOCKET_HELPER_H_
#include <string>
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

View file

@ -46,6 +46,18 @@ SocketMessenger::SocketMessenger(std::shared_ptr<ba::local::stream_protocol::soc
socket->set_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<serialization_buffer_size> whole_message{length};

View file

@ -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<boost::asio::local::stream_protocol::socket> 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;

View file

@ -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

View file

@ -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<std::int32_t>(event.wheel.direction) });
mouse_events.push_back({ EV_REL, REL_WHEEL, static_cast<std::int32_t>(event.wheel.y) });
break;
case SDL_KEYDOWN: {
const auto code = convert_sdl_scancode_to_evdev(event.key.keysym.scancode);

View file

@ -25,29 +25,34 @@ namespace anbox {
namespace ubuntu {
WindowCreator::WindowCreator(const std::shared_ptr<input::Manager> &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;

View file

@ -48,7 +48,8 @@ private:
std::shared_ptr<input::Manager> input_manager_;
std::map<EGLNativeWindowType,std::shared_ptr<Window>> windows_;
std::shared_ptr<Window> current_window_;
std::thread event_thread;
std::thread event_thread_;
bool event_thread_running_;
};
} // namespace bridge
} // namespace anbox

View file

@ -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<std::string> &paths) {
for (const auto &path: paths) {
if (!fs::is_directory(fs::path(path)))
fs::create_directories(fs::path(path));
}
}
} // namespace utils
} // namespace anbox

View file

@ -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<std::string> &paths);
template<typename... Types>
static std::string string_format(const std::string& fmt_str, Types&&... args);
} // namespace utils

View file

@ -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;
}