Merge pull request #214 from morphis/f/fork-session-mgr

Start session manager when not already running
This commit is contained in:
Simon Fels 2017-05-07 12:05:53 +02:00 committed by GitHub
commit da20611a6c
16 changed files with 290 additions and 40 deletions

View file

@ -64,6 +64,7 @@ find_package(GLESv2 REQUIRED)
find_package(Protobuf REQUIRED)
pkg_check_modules(SDL2 sdl2 REQUIRED)
pkg_check_modules(SDL2_IMAGE SDL2_image REQUIRED)
pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
pkg_check_modules(DBUS dbus-1 REQUIRED)
pkg_check_modules(LXC lxc REQUIRED)
@ -114,3 +115,6 @@ if (NOT "${HOST_CMAKE_C_COMPILER}" STREQUAL "")
message(STATUS "Host C compiler: ${HOST_CMAKE_C_COMPILER}")
message(STATUS "Host C compiler: ${HOST_CMAKE_CXX_COMPILER}")
endif()
install(FILES data/ui/loading-screen.png DESTINATION share/anbox/ui)

BIN
data/ui/loading-screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -114,6 +114,12 @@ public:
*/
wait::Result wait_for(const wait::Flags& flags);
/**
* @brief Mark the child process to not to be killed when the ChildProcess
* instance goes away.
*/
void dont_kill_on_cleanup();
#ifndef ANDROID
/**
* @brief Access this process's stderr.

View file

@ -303,7 +303,7 @@ struct ChildProcess::Private
~Private()
{
// Check if we are in the original parent process.
if (original_parent_pid == getpid())
if (original_parent_pid == getpid() && !dont_kill_on_cleanup)
{
// If so, check if we are considering a valid pid here.
// If so, we kill the original child.
@ -333,6 +333,8 @@ struct ChildProcess::Private
// is called from the child process.
pid_t original_parent_pid;
pid_t original_child_pid;
bool dont_kill_on_cleanup = false;
};
ChildProcess ChildProcess::invalid()
@ -395,6 +397,11 @@ wait::Result ChildProcess::wait_for(const wait::Flags& flags)
return result;
}
void ChildProcess::dont_kill_on_cleanup()
{
d->dont_kill_on_cleanup = true;
}
#ifndef ANDROID
std::istream& ChildProcess::cerr()
{

View file

@ -32,6 +32,7 @@ apt-get install -qq -y \
libproperties-cpp-dev \
libprotobuf-dev \
libsdl2-dev \
libsdl2-image-dev \
lxc-dev \
pkg-config \
protobuf-compiler

View file

@ -116,6 +116,7 @@ parts:
- libprotobuf-dev
- libproperties-cpp-dev
- libsdl2-dev
- libsdl2-image-dev
- pkg-config
- protobuf-compiler
stage-packages:
@ -128,4 +129,5 @@ parts:
make test
prime:
- usr/bin/anbox
- usr/share/anbox
- usr/lib/*-linux-*/

View file

@ -7,6 +7,7 @@ include_directories(
${DBUS_CPP_INCLUDE_DIRS}
${DBUS_INCLUDE_DIRS}
${SDL2_INCLUDE_DIRS}
${SDL2_IMAGE_INCLUDE_DIRS}
${LXC_INCLUDE_DIRS}
${MIRCLIENT_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
@ -206,6 +207,8 @@ set(SOURCES
anbox/utils/environment_file.cpp
anbox/ui/splash_screen.cpp
anbox/do_not_copy_or_move.h
anbox/optional.h
anbox/defer_action.h)
@ -218,6 +221,8 @@ target_link_libraries(anbox-core
${DBUS_CPP_LIBRARIES}
${SDL2_LDFLAGS}
${SDL2_LIBRARIES}
${SDL2_IMAGE_LDFLAGS}
${SDL2_IMAGE_LIBRARIES}
${LXC_LDFLAGS}
${LXC_LIBRARIES}
${MIRCLIENT_LDFLAGS}

View file

@ -22,16 +22,25 @@
namespace anbox {
namespace android {
std::ostream &operator<<(std::ostream &out, const Intent &intent) {
out << "["
<< "action=" << intent.action << " "
<< "uri=" << intent.uri << " "
<< "type=" << intent.type << " "
<< "flags=" << intent.flags << " "
<< "package=" << intent.package << " "
<< "component=" << intent.component << " "
<< "categories=[ ";
for (const auto &category : intent.categories) out << category << " ";
out << "]]";
out << "[";
if (!intent.action.empty())
out << " " << "action=" << intent.action << " ";
if (!intent.uri.empty())
out << " " << "uri=" << intent.uri << " ";
if (!intent.type.empty())
out << " " << "type=" << intent.type << " ";
if (intent.flags > 0)
out << " " << "flags=" << intent.flags << " ";
if (!intent.package.empty())
out << " " << "package=" << intent.package << " ";
if (!intent.component.empty())
out << "component=" << intent.component << " ";
if (intent.categories.size() > 0) {
out << "categories=[ ";
for (const auto &category : intent.categories) out << category << " ";
out << "] ";
}
out << "]";
return out;
}
} // namespace android

View file

@ -19,6 +19,8 @@
#include "anbox/common/wait_handle.h"
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/common/dispatcher.h"
#include "anbox/ui/splash_screen.h"
#include "anbox/config.h"
#include "anbox/runtime.h"
#include "anbox/logger.h"
@ -26,12 +28,31 @@
#include <boost/filesystem.hpp>
#include "core/posix/exec.h"
#include "core/posix/fork.h"
#include "core/posix/signal.h"
namespace fs = boost::filesystem;
namespace {
const boost::posix_time::seconds max_wait_timeout{30};
const boost::posix_time::seconds max_wait_timeout{240};
const int max_restart_attempts{3};
const std::chrono::seconds restart_interval{5};
}
bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub) {
try {
DEBUG("Sending launch intent %s to Android ..", intent_);
stub->launch(intent_, graphics::Rect::Invalid, stack_);
} catch (const std::exception &err) {
ERROR("Failed to launch activity: %s", err.what());
return false;
} catch (...) {
ERROR("Failed to launch activity");
return false;
}
return true;
}
anbox::cmds::Launch::Launch()
@ -69,42 +90,91 @@ anbox::cmds::Launch::Launch()
auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
const auto snap_path = utils::get_env_value("SNAP");
if (!snap_path.empty()) {
const auto resource_path = fs::path(snap_path) / "usr" / "share" / "anbox";
SystemConfiguration::instance().set_resource_path(resource_path);
}
std::shared_ptr<ui::SplashScreen> ss;
// Instead of relying on the user session init system to start our
// session manager process we also attempt to start it on our own
// if not already running. This will help to mitigate problems with
// a crashing or a not yet started session manager instance.
std::shared_ptr<dbus::stub::ApplicationManager> stub;
try {
stub = dbus::stub::ApplicationManager::create_for_bus(bus);
} catch (...) {
ERROR("Anbox session manager service isn't running!");
for (auto n = 0; n < max_restart_attempts; n++) {
try {
stub = dbus::stub::ApplicationManager::create_for_bus(bus);
break;
} catch (std::exception &err) {
WARNING("Anbox session manager service isn't running, trying to start it.");
// Give us a splash screen as long as we're trying to connect
// with the session manager so the user knows something is
// happening after he started Anbox.
if (!ss)
ss = std::make_shared<ui::SplashScreen>();
std::vector<std::string> args = {"session-manager"};
std::map<std::string,std::string> env;
core::posix::this_process::env::for_each([&](const std::string &name, const std::string &value) {
env.insert({name, value});
});
const auto exe_path = utils::process_get_exe_path(::getpid());
if (!fs::exists(exe_path)) {
ERROR("Can't find correct anbox executable to run. Found %s but does not exist", exe_path);
return EXIT_FAILURE;
}
try {
const auto flags = core::posix::StandardStream::stdout | core::posix::StandardStream::stderr;
auto child = core::posix::fork([&]() {
auto grandchild = core::posix::exec(exe_path, args, env, flags);
grandchild.dont_kill_on_cleanup();
return core::posix::exit::Status::success;
}, flags);
// We don't wait for the grandchild but the child as we use double forking
// here to break through the process hierarchy and make the grandchild a
// direct child of the init process so it keeps running on its own and
// indepent of our short living process here.
child.wait_for(core::posix::wait::Flags::untraced);
DEBUG("Started session manager, will now try to connect ..");
}
catch (...) {
ERROR("Failed to start session manager instance");
}
std::this_thread::sleep_for(restart_interval);
}
}
if (!stub) {
ERROR("Couldn't get a connection with the session manager");
return EXIT_FAILURE;
}
auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
bool success = false;
auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
dispatcher->dispatch([&]() {
if (stub->ready()) {
try {
stub->launch(intent_, graphics::Rect::Invalid, stack_);
success = true;
} catch (std::exception &err) {
ERROR("err %s", err.what());
}
ss.reset();
success = try_launch_activity(stub);
trap->stop();
return;
}
DEBUG("Android hasn't fully booted yet. Waiting a bit..");
DEBUG("Android hasn't fully booted yet. Waiting a bit ..");
stub->ready().changed().connect([&](bool ready) {
if (!ready)
return;
try {
stub->launch(intent_, graphics::Rect::Invalid, stack_);
success = true;
} catch (std::exception &err) {
ERROR("Failed to launch activity: %s", err.what());
success = false;
}
ss.reset();
success = try_launch_activity(stub);
trap->stop();
});
});
@ -112,8 +182,10 @@ anbox::cmds::Launch::Launch()
boost::asio::deadline_timer timer(rt->service());
timer.expires_from_now(max_wait_timeout);
timer.async_wait([&](const boost::system::error_code&) {
WARNING("Stop waiting as we're already waiting for too long. Something is wrong");
WARNING("with your setup and the container may have failed to boot.");
WARNING("Stopped waiting as we've already waited for too long. Something");
WARNING("is wrong with your setup or the container has failed to boot.");
WARNING("If you think you found a bug please don't hesitate to file on");
WARNING("at https://github.com/anbox/anbox/issues/new");
trap->stop();
});

View file

@ -23,6 +23,7 @@
#include <memory>
#include "anbox/android/intent.h"
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/wm/stack.h"
#include "anbox/cli.h"
@ -33,6 +34,8 @@ class Launch : public cli::CommandWithFlagsAndAction {
Launch();
private:
bool try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub);
android::Intent intent_;
wm::Stack::Id stack_;
};

View file

@ -41,6 +41,10 @@ void anbox::SystemConfiguration::set_data_path(const std::string &path) {
data_path = path;
}
void anbox::SystemConfiguration::set_resource_path(const fs::path &path) {
resource_path = path;
}
fs::path anbox::SystemConfiguration::data_dir() const {
return data_path;
}
@ -76,6 +80,10 @@ std::string anbox::SystemConfiguration::application_item_dir() const {
return dir.string();
}
std::string anbox::SystemConfiguration::resource_dir() const {
return resource_path.string();
}
anbox::SystemConfiguration& anbox::SystemConfiguration::instance() {
static SystemConfiguration config;
return config;

View file

@ -31,6 +31,7 @@ class SystemConfiguration {
virtual ~SystemConfiguration() = default;
void set_data_path(const std::string &path);
void set_resource_path(const boost::filesystem::path &path);
boost::filesystem::path data_dir() const;
std::string rootfs_dir() const;
@ -40,11 +41,13 @@ class SystemConfiguration {
std::string container_socket_path() const;
std::string input_device_dir() const;
std::string application_item_dir() const;
std::string resource_dir() const;
protected:
SystemConfiguration() = default;
boost::filesystem::path data_path = "/var/lib/anbox";
boost::filesystem::path resource_path = "/usr/share/anbox";
};
} // namespace anbox

View file

@ -25,10 +25,8 @@ namespace skeleton {
std::shared_ptr<Service> Service::create_for_bus(
const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::application::Manager> &application_manager) {
auto service = core::dbus::Service::add_service(
bus, anbox::dbus::interface::Service::name());
auto object =
service->add_object_for_path(anbox::dbus::interface::Service::path());
auto service = core::dbus::Service::add_service(bus, anbox::dbus::interface::Service::name());
auto object = service->add_object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<Service>(bus, service, object, application_manager);
}

View file

@ -25,7 +25,7 @@ namespace dbus {
namespace stub {
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(const core::dbus::Bus::Ptr &bus) {
auto service = core::dbus::Service::use_service_or_throw_if_not_available(bus, anbox::dbus::interface::Service::name());
auto object = service->add_object_for_path(anbox::dbus::interface::Service::path());
auto object = service->object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<ApplicationManager>(bus, service, object);
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2017 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/ui/splash_screen.h"
#include "anbox/config.h"
#include "anbox/utils.h"
#include <SDL2/SDL_image.h>
namespace anbox {
namespace ui {
SplashScreen::SplashScreen() {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
const auto message = utils::string_format("Failed to initialize SDL: %s", SDL_GetError());
BOOST_THROW_EXCEPTION(std::runtime_error(message));
}
const auto width = 1024, height = 768;
window_ = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS);
if (!window_) {
const auto message = utils::string_format("Failed to create window: %s", SDL_GetError());
BOOST_THROW_EXCEPTION(std::runtime_error(message));
}
auto surface = SDL_GetWindowSurface(window_);
if (!surface)
BOOST_THROW_EXCEPTION(std::runtime_error("Could not retrieve surface for our window"));
SDL_FillRect(surface, nullptr, SDL_MapRGB(surface->format, 0xee, 0xee, 0xee));
SDL_UpdateWindowSurface(window_);
auto renderer = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED);
if (!renderer)
BOOST_THROW_EXCEPTION(std::runtime_error("Could not create renderer"));
const auto icon_path = utils::string_format("%s/ui/loading-screen.png", SystemConfiguration::instance().resource_dir());
auto img = IMG_LoadTexture(renderer, icon_path.c_str());
if (!img) {
const auto msg = utils::string_format("Failed to create texture from %s", icon_path);
BOOST_THROW_EXCEPTION(std::runtime_error(msg));
}
SDL_RenderClear(renderer);
SDL_Rect r{0, 0, width, height};
SDL_RenderCopy(renderer, img, nullptr, &r);
SDL_RenderPresent(renderer);
SDL_ShowWindow(window_);
event_thread_ = std::thread(&SplashScreen::process_events, this);
}
SplashScreen::~SplashScreen() {
if (event_thread_running_) {
event_thread_running_ = false;
if (event_thread_.joinable())
event_thread_.join();
}
if (window_)
SDL_DestroyWindow(window_);
}
void SplashScreen::process_events() {
event_thread_running_ = true;
while (event_thread_running_) {
SDL_Event event;
while (SDL_WaitEventTimeout(&event, 100)) {
// Keep running until we're terminated
}
}
}
} // namespace ui
} // namespace anbox

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 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_UI_SPLASH_SCREEN_H_
#define ANBOX_UI_SPLASH_SCREEN_H_
#include <thread>
#include <SDL2/SDL.h>
namespace anbox {
namespace ui {
class SplashScreen {
public:
SplashScreen();
~SplashScreen();
private:
void process_events();
std::thread event_thread_;
bool event_thread_running_;
SDL_Window *window_;
};
} // namespace ui
} // namespace anbox
#endif