anbox/src/anbox/cmds/container_manager.cpp

149 lines
4.8 KiB
C++

/*
* 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/common/loop_device_allocator.h"
#include "anbox/logger.h"
#include "anbox/runtime.h"
#include "anbox/config.h"
#include "core/posix/signal.h"
#include <sys/mount.h>
#include <linux/loop.h>
#include <fcntl.h>
namespace fs = boost::filesystem;
namespace {
constexpr unsigned int unprivileged_user_id{100000};
}
anbox::cmds::ContainerManager::ContainerManager()
: CommandWithFlagsAndAction{
cli::Name{"container-manager"}, cli::Usage{"container-manager"},
cli::Description{"Start the container manager service"}} {
flag(cli::make_flag(cli::Name{"android-image"},
cli::Description{"Path to the Android rootfs image file if not stored in the data path"},
android_img_path_));
flag(cli::make_flag(cli::Name{"data-path"},
cli::Description{"Path where the container and its data is stored"},
data_path_));
flag(cli::make_flag(cli::Name{"privileged"},
cli::Description{"Run Android container in privileged mode"},
privileged_));
action([&](const cli::Command::Context&) {
try {
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();
});
if (!data_path_.empty())
SystemConfiguration::instance().set_data_path(data_path_);
if (!setup_mounts())
return EXIT_FAILURE;
auto rt = Runtime::create();
auto service = container::Service::create(rt, privileged_);
rt->start();
trap->run();
rt->stop();
return EXIT_SUCCESS;
} catch (std::exception &err) {
ERROR("%s", err.what());
return EXIT_FAILURE;
}
});
}
anbox::cmds::ContainerManager::~ContainerManager() {}
bool anbox::cmds::ContainerManager::setup_mounts() {
fs::path android_img_path = android_img_path_;
if (android_img_path.empty())
android_img_path = SystemConfiguration::instance().data_dir() / "android.img";
if (!fs::exists(android_img_path)) {
ERROR("Android image does not exist at path %s", android_img_path);
return false;
}
const auto android_rootfs_dir = SystemConfiguration::instance().rootfs_dir();
if (utils::is_mounted(android_rootfs_dir)) {
ERROR("Androd rootfs is already mounted!?");
return false;
}
if (!fs::exists(android_rootfs_dir))
fs::create_directory(android_rootfs_dir);
auto loop_device = common::LoopDeviceAllocator::new_device();
if (!loop_device)
return false;
if (!loop_device->attach_file(android_img_path)) {
ERROR("Failed to attach Android rootfs image to loopback device");
return false;
}
auto m = common::MountEntry::create(loop_device, android_rootfs_dir, "squashfs", MS_MGC_VAL | MS_RDONLY);
if (!m) {
ERROR("Failed to mount Android rootfs");
return false;
}
mounts_.push_back(m);
for (const auto &dir_name : std::vector<std::string>{"cache", "data"}) {
auto target_dir_path = fs::path(android_rootfs_dir) / dir_name;
auto src_dir_path = SystemConfiguration::instance().data_dir() / dir_name;
if (!fs::exists(src_dir_path)) {
if (!fs::create_directory(src_dir_path)) {
ERROR("Failed to create Android %s directory", dir_name);
mounts_.clear();
return false;
}
if (::chown(src_dir_path.c_str(), unprivileged_user_id, unprivileged_user_id) != 0) {
ERROR("Failed to allow access for unprivileged user on %s directory of the rootfs", dir_name);
mounts_.clear();
return false;
}
}
auto m = common::MountEntry::create(src_dir_path, target_dir_path, "", MS_MGC_VAL | MS_BIND);
if (!m) {
ERROR("Failed to mount Android %s directory", dir_name);
mounts_.clear();
return false;
}
mounts_.push_back(m);
}
// Unmounting needs to happen in reverse order
std::reverse(mounts_.begin(), mounts_.end());
return true;
}