A bunch more changes to get the whole system properly booted

This commit is contained in:
Simon Fels 2016-06-19 13:26:32 +02:00
commit 720b71f1e8
58 changed files with 1835 additions and 532 deletions

View file

@ -62,6 +62,7 @@ find_package(EGL REQUIRED)
find_package(GLESv2 REQUIRED)
pkg_check_modules(MIRCLIENT REQUIRED mirclient)
pkg_check_modules(LIBEVDEV REQUIRED libevdev)
#####################################################################
# Enable code coverage calculation with gcov/gcovr/lcov

View file

@ -1,4 +1,3 @@
add_subdirectory(mir_support)
add_subdirectory(GLESv1_dec)
add_subdirectory(GLESv2_dec)
add_subdirectory(libOpenGLESDispatch)

View file

@ -14,5 +14,11 @@ add_custom_command(
set(SOURCES
GLESv1Decoder.cpp)
if ("${cmake_build_type_lower}" STREQUAL "debug")
set(OPENGL_DEBUG "-DOPENGL_DEBUG_PRINTOUT -DCHECK_GL_ERROR")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPENGL_DEBUG}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}")
endif()
add_library(GLESv1_dec ${SOURCES} ${GENERATED_SOURCES})
target_link_libraries(GLESv1_dec OpenglCodecCommon)

View file

@ -14,5 +14,11 @@ add_custom_command(
set(SOURCES
GLESv2Decoder.cpp)
if ("${cmake_build_type_lower}" STREQUAL "debug")
set(OPENGL_DEBUG "-DOPENGL_DEBUG_PRINTOUT -DCHECK_GL_ERROR")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPENGL_DEBUG}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}")
endif()
add_library(GLESv2_dec ${SOURCES} ${GENERATED_SOURCES})
target_link_libraries(GLESv2_dec OpenglCodecCommon)

View file

@ -2,7 +2,7 @@ set(SOURCES
ColorBuffer.cpp
FbConfig.cpp
FrameBuffer.cpp
NativeSubWindow_mir.cpp
NativeSubWindow_delegate.cpp
ReadBuffer.cpp
RenderContext.cpp
RenderControl.cpp
@ -19,9 +19,6 @@ set(SOURCES
UnixStream.cpp
WindowSurface.cpp)
include_directories(BEFORE
${MIRCLIENT_INCLUDE_DIRS})
add_library(OpenglRender ${SOURCES})
target_link_libraries(OpenglRender
emugl_common
@ -30,8 +27,5 @@ target_link_libraries(OpenglRender
renderControl_dec
OpenGLESDispatch
OpenglCodecCommon
mir_support
${EGL_LDFLAGS}
${EGL_LIBRARIES}
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES})
${EGL_LIBRARIES})

View file

@ -28,10 +28,6 @@
#include <stdio.h>
#include "mir_support/shared_state.h"
#define MID_AUBERGINE(x) (x)*0.368627451f, (x)*0.152941176f, (x)*0.31372549f
namespace {
// Helper class to call the bind_locked() / unbind_locked() properly.
@ -188,8 +184,7 @@ bool FrameBuffer::initialize(int width, int height, bool useSubWindow)
//
// Initialize backend EGL display
//
mir::support::SharedState::get()->ensure_connection();
fb->m_eglDisplay = s_egl.eglGetDisplay(mir::support::SharedState::get()->native_display());
fb->m_eglDisplay = s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (fb->m_eglDisplay == EGL_NO_DISPLAY) {
ERR("Failed to Initialize backend EGL display\n");
delete fb;
@ -582,7 +577,6 @@ bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window,
if (m_lastPostedColorBuffer) {
post(m_lastPostedColorBuffer, false);
} else {
s_gles2.glClearColor(MID_AUBERGINE(1.0), 1.0);
s_gles2.glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);

View file

@ -20,10 +20,21 @@
#include <EGL/egl.h>
#include <memory>
#ifdef __cplusplus
extern "C" {
#endif
class SubWindowHandler {
public:
virtual ~SubWindowHandler() { }
virtual EGLNativeWindowType create_window(int x, int y, int width, int height) = 0;
virtual void destroy_window(EGLNativeWindowType win) = 0;
};
void registerSubWindowHandler(const std::shared_ptr<SubWindowHandler> &handler);
typedef void (*SubWindowRepaintCallback)(void*);
// Create a new sub-window that will be used to display the content of the

View file

@ -0,0 +1,72 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "NativeSubWindow.h"
#include <stdexcept>
#include <string>
#include <map>
namespace {
static std::shared_ptr<SubWindowHandler> current_handler = nullptr;
}
void registerSubWindowHandler(const std::shared_ptr<SubWindowHandler> &handler) {
if (current_handler)
throw std::runtime_error("A sub window handle is already registered");
current_handler = handler;
}
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param) {
(void) p_window;
(void) repaint_callback;
(void) repaint_callback_param;
if (!current_handler)
return (EGLNativeWindowType) 0;
return current_handler->create_window(x, y, width, height);
}
void destroySubWindow(EGLNativeWindowType win) {
if (!current_handler)
return;
return current_handler->destroy_window(win);
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
(void) p_parent_window;
(void) p_sub_window;
(void) x;
(void) y;
(void) width;
(void) height;
return true;
}

View file

@ -1,10 +0,0 @@
set(SOURCES
shared_state.cpp)
include_directories(BEFORE
${MIRCLIENT_INCLUDE_DIRS})
add_library(mir_support ${SOURCES})
target_link_libraries(mir_support
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES})

View file

@ -1,74 +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 "mir_support/shared_state.h"
namespace mir {
namespace support {
std::shared_ptr<SharedState> SharedState::get() {
static auto instance = std::make_shared<SharedState>();
return instance;
}
SharedState::SharedState() :
connection_(nullptr) {
}
SharedState::~SharedState() {
release_connection();
}
void SharedState::ensure_connection() {
if (connection_)
return;
auto xdg_runtime_dir = ::getenv("XDG_RUNTIME_DIR");
if (!xdg_runtime_dir)
throw std::runtime_error("Failed to find XDG_RUNTIME_DIR");
std::string socket_path = xdg_runtime_dir;
socket_path += "/mir_socket";
connection_ = mir_connect_sync(socket_path.c_str(), "anbox");
if (!mir_connection_is_valid(connection_)) {
std::string msg;
msg += "Failed to connect with Mir server: ";
msg += mir_connection_get_error_message(connection_);
msg += "\n";
throw std::runtime_error(msg.c_str());
}
}
void SharedState::release_connection() {
if (!connection_)
return;
mir_connection_release(connection_);
connection_ = nullptr;
}
MirConnection* SharedState::connection() const {
return connection_;
}
EGLNativeDisplayType SharedState::native_display() const {
return mir_connection_get_egl_native_display(connection_);
}
} // namespace support
} // namespace mir

View file

@ -10,5 +10,11 @@ add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS emugen)
if ("${cmake_build_type_lower}" STREQUAL "debug")
set(OPENGL_DEBUG "-DOPENGL_DEBUG_PRINTOUT -DCHECK_GL_ERROR")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPENGL_DEBUG}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}")
endif()
add_library(renderControl_dec ${GENERATED_SOURCES})
target_link_libraries(renderControl_dec OpenglCodecCommon)

View file

@ -1319,10 +1319,6 @@ bwrap_main (int argc,
/* Get the (optional) capabilities we need, drop root */
acquire_caps ();
/* Never gain any more privs during exec */
if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
die_with_error ("prctl(PR_SET_NO_NEW_CAPS) failed");
/* The initial code is run with high permissions
(i.e. CAP_SYS_ADMIN), so take lots of care. */
@ -1390,9 +1386,7 @@ bwrap_main (int argc,
/* We block sigchild here so that we can use signalfd in the monitor. */
block_sigchild ();
clone_flags = SIGCHLD | CLONE_NEWNS;
if (opt_unshare_user)
clone_flags |= CLONE_NEWUSER;
clone_flags = SIGCHLD;
if (opt_unshare_pid)
clone_flags |= CLONE_NEWPID;
if (opt_unshare_net)
@ -1423,14 +1417,6 @@ bwrap_main (int argc,
pid = raw_clone (clone_flags, NULL);
if (pid == -1)
{
if (opt_unshare_user)
{
if (errno == EINVAL)
die ("Creating new namespace failed, likely because the kernel does not support user namespaces. bwrap must be installed setuid on such systems.");
else if (errno == EPERM && !is_privileged)
die ("No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'.");
}
die_with_error ("Creating new namespace failed");
}
@ -1439,18 +1425,6 @@ bwrap_main (int argc,
if (pid != 0)
{
if (is_privileged && opt_unshare_user)
{
/* Map the uid/gid 0 if opt_needs_devpts, as otherwise
* mounting it will fail.
* Due to this non-direct mapping we need to have set[ug]id
* caps in the parent namespaces, and thus we need to write
* the map in the parent namespace, not the child. */
write_uid_gid_map (ns_uid, uid,
ns_gid, gid,
pid, TRUE, opt_needs_devpts);
}
/* Initial launched process, wait for exec:ed command to exit */
if (opt_pid_file) {
@ -1464,9 +1438,6 @@ bwrap_main (int argc,
close(pid_file_fd);
}
/* We don't need any caps in the launcher, drop them immediately. */
drop_caps ();
/* Let child run */
val = 1;
res = write (child_wait_fd, &val, 8);
@ -1486,25 +1457,6 @@ bwrap_main (int argc,
ns_uid = opt_sandbox_uid;
ns_gid = opt_sandbox_gid;
if (!is_privileged && opt_unshare_user)
{
/* In the unprivileged case we have to write the uid/gid maps in
* the child, because we have no caps in the parent */
if (opt_needs_devpts)
{
/* This is a bit hacky, but we need to first map the real uid/gid to
0, otherwise we can't mount the devpts filesystem because root is
not mapped. Later we will create another child user namespace and
map back to the real uid */
ns_uid = 0;
ns_gid = 0;
}
write_uid_gid_map (ns_uid, uid,
ns_gid, gid,
-1, TRUE, FALSE);
}
old_umask = umask (0);
@ -1542,53 +1494,7 @@ bwrap_main (int argc,
if (chdir ("/") != 0)
die_with_error ("chdir / (base path)");
if (is_privileged)
{
pid_t child;
int privsep_sockets[2];
if (socketpair (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, privsep_sockets) != 0)
die_with_error ("Can't create privsep socket");
child = fork ();
if (child == -1)
die_with_error ("Can't fork unprivileged helper");
if (child == 0)
{
/* Unprivileged setup process */
drop_caps ();
close (privsep_sockets[0]);
setup_newroot (opt_unshare_pid, privsep_sockets[1]);
exit (0);
}
else
{
uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */
uint32_t op, flags;
const char *arg1, *arg2;
cleanup_fd int unpriv_socket = -1;
unpriv_socket = privsep_sockets[0];
close (privsep_sockets[1]);
do
{
op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer),
&flags, &arg1, &arg2);
privileged_op (-1, op, flags, arg1, arg2);
if (write (unpriv_socket, buffer, 1) != 1)
die ("Can't write to op_socket");
}
while (op != PRIV_SEP_OP_DONE);
/* Continue post setup */
}
}
else
{
setup_newroot (opt_unshare_pid, -1);
}
setup_newroot (opt_unshare_pid, -1);
/* The old root better be rprivate or we will send unmount events to the parent namespace */
if (mount ("oldroot", "oldroot", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
@ -1597,21 +1503,6 @@ bwrap_main (int argc,
if (umount2 ("oldroot", MNT_DETACH))
die_with_error ("unmount old root");
if (opt_unshare_user &&
(ns_uid != opt_sandbox_uid || ns_gid != opt_sandbox_gid))
{
/* Now that devpts is mounted and we've no need for mount
permissions we can create a new userspace and map our uid
1:1 */
if (unshare (CLONE_NEWUSER))
die_with_error ("unshare user ns");
write_uid_gid_map (opt_sandbox_uid, ns_uid,
opt_sandbox_gid, ns_gid,
-1, FALSE, FALSE);
}
/* Now make /newroot the real root */
if (chdir ("/newroot") != 0)
die_with_error ("chdir newroot");
@ -1620,31 +1511,6 @@ bwrap_main (int argc,
if (chdir ("/") != 0)
die_with_error ("chdir /");
/* Now we have everything we need CAP_SYS_ADMIN for, so drop it */
drop_caps ();
if (opt_seccomp_fd != -1)
{
cleanup_free char *seccomp_data = NULL;
size_t seccomp_len;
struct sock_fprog prog;
seccomp_data = load_file_data (opt_seccomp_fd, &seccomp_len);
if (seccomp_data == NULL)
die_with_error ("Can't read seccomp data");
if (seccomp_len % 8 != 0)
die ("Invalide seccomp data, must be multiple of 8");
prog.len = seccomp_len / 8;
prog.filter = (struct sock_filter *) seccomp_data;
close (opt_seccomp_fd);
if (prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0)
die_with_error ("prctl(PR_SET_SECCOMP)");
}
umask (old_umask);
new_cwd = "/";

View file

@ -3,6 +3,9 @@ include_directories(
${GLIB_INCLUDE_DIRS}
${GIO_INCLUDE_DIRS}
${GIO-UNIX_INCLUDE_DIRS}
${MIRCLIENT_INCLUDE_DIRS}
${LIBEVDEV_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/external/process-cpp-minimal/include
${CMAKE_SOURCE_DIR}/external/android-emugl/host/include
@ -16,9 +19,12 @@ set(SOURCES
anbox/version.cpp
anbox/daemon.cpp
anbox/config.cpp
anbox/not_reachable.cpp
anbox/pid_persister.cpp
anbox/namespace_attacher.cpp
anbox/container.cpp
anbox/container_connector.cpp
anbox/input_channel.cpp
anbox/common/fd.cpp
anbox/common/fd_sets.h
@ -37,11 +43,18 @@ set(SOURCES
anbox/network/delegate_message_processor.h
anbox/graphics/opengles_message_processor.cpp
anbox/graphics/mir_display_connection.cpp
anbox/graphics/mir_window.cpp
anbox/graphics/mir_native_window_creator.cpp
anbox/graphics/gl_renderer_server.cpp
anbox/support/null_message_processor.cpp
anbox/support/qemud_message_processor.cpp
anbox/support/boot_properties_message_processor.cpp
anbox/support/hwcontrol_message_processor.cpp
anbox/support/sensors_message_processor.cpp
anbox/support/camera_message_processor.cpp
anbox/support/fingerprint_message_processor.cpp
anbox/cmds/version.cpp
anbox/cmds/run.cpp
@ -55,15 +68,22 @@ add_library(anbox-core ${SOURCES})
target_link_libraries(anbox-core
${Boost_LDFLAGS}
${Boost_LIBRARIES}
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES}
${LIBEVDEV_LDFLAGS}
${LIBEVDEV_LIBRARIES}
pthread
process-cpp
bwrap
OpenglRender)
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
RUNTIME DESTINATION sbin

View file

@ -28,6 +28,7 @@
#include "anbox/network/published_socket_connector.h"
#include "anbox/network/qemu_pipe_connection_creator.h"
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/input_channel.h"
#include <sys/prctl.h>
@ -66,7 +67,9 @@ anbox::cmds::Run::Run()
auto rt = Runtime::create();
auto renderer = std::make_shared<graphics::GLRendererServer>();
auto input_channel = std::make_shared<InputChannel>();
auto renderer = std::make_shared<graphics::GLRendererServer>(input_channel);
renderer->start();
// Socket which will be used by the qemud service inside the Android
@ -87,7 +90,11 @@ anbox::cmds::Run::Run()
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.temporary_dirs.push_back("/data");
spec.temporary_dirs.push_back("/cache");
spec.temporary_dirs.push_back("/storage");
spec.temporary_dirs.push_back("/dev/input");
// 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.
@ -95,6 +102,11 @@ anbox::cmds::Run::Run()
// 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");
// Our uinput based event node should get root:android_input assigned on Ubuntu
// which is enough for our phablet user (being root inside the container) to
// read event data from it.
spec.dev_bind_paths.push_back({input_channel->dev_path()});
spec.dev_bind_paths.push_back({"/dev/input/event7"});
auto container = Container::create(spec);

View file

@ -27,10 +27,12 @@
namespace fs = boost::filesystem;
anbox::cmds::Shell::Shell()
: CommandWithFlagsAndAction{cli::Name{"shell"}, cli::Usage{"shell"}, cli::Description{"Open a shell within the Anbox container"}}
: CommandWithFlagsAndAction{cli::Name{"shell"}, cli::Usage{"shell"}, cli::Description{"Open a shell within the Anbox container"}},
pid_(-1)
{
action([this](const cli::Command::Context &ctx) {
ContainerConnector connector;
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");
});
}

View file

@ -31,7 +31,7 @@ public:
Shell();
private:
std::string rootfs_;
int pid_;
};
} // namespace cmds
} // namespace anbox

View file

@ -19,6 +19,9 @@
#include <boost/filesystem.hpp>
#include <fstream>
#include <sstream>
#include "core/posix/fork.h"
#include "core/posix/exec.h"
@ -28,14 +31,15 @@
#include "anbox/container.h"
#include "anbox/common/fd.h"
extern "C" int bwrap_main(int argc, char **argv);
#include <grp.h>
#include <sys/mount.h>
namespace fs = boost::filesystem;
namespace anbox {
Container::Spec Container::Spec::Default() {
Spec spec;
spec.init_command = "/init";
spec.init_command = "/anbox-init.sh";
spec.environment.insert({"PATH", "/system/bin:/system/sbin:/system/xbin"});
return spec;
}
@ -53,66 +57,76 @@ Container::~Container() {
stop();
}
std::vector<Container::IdMapping> Container::read_id_mappings() {
std::vector<Container::IdMapping> mappings;
static const std::string subuid_path = "/etc/subuid";
static const std::string subgid_path = "/etc/subgid";
std::ifstream subuid_file(subuid_path);
return mappings;
}
void Container::start() {
std::vector<std::string> arguments = {
"--ro-bind", spec_.rootfs_path, "/",
DEBUG("uid %d gid %d", getuid(), getgid());
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.
"-m", utils::string_format("u:0:%d:1", getuid()),
"-m", utils::string_format("g:0:%d:1", getgid()),
// FIXME(morphis): We need to determine those things dynamically and
// error out if not subui range is set for our current user.
"-m", "u:1:100000:100000",
"-m", "g:1:100000:100000",
"--",
// FIXME(morphis): use system or in-click path
"/home/phablet/anbox-container",
"--bind", spec_.rootfs_path, "/",
"--dev", "/dev",
"--proc", "/proc",
"--unshare-user",
"--unshare-ipc",
"--unshare-pid",
"--unshare-net",
"--unshare-uts",
// We will take UID 0 (root) inside the container
"--uid", "0",
// We will take GID 0 (root) inside the container
"--gid", "0",
"--chdir", "/",
"--pid-file", utils::string_format("%s/pid", config::data_path()),
};
for (const auto &dir : spec_.temporary_dirs) {
arguments.push_back("--tmpfs");
arguments.push_back(dir);
args.push_back("--tmpfs");
args.push_back(dir);
}
for (const auto &path : spec_.dev_bind_paths) {
arguments.push_back("--dev-bind");
arguments.push_back(path);
arguments.push_back(path);
args.push_back("--dev-bind");
args.push_back(path);
args.push_back(path);
}
for (const auto &path : spec_.bind_paths) {
arguments.push_back("--bind");
arguments.push_back(path.first);
arguments.push_back(path.second);
args.push_back("--bind");
args.push_back(path.first);
args.push_back(path.second);
}
for (const auto &env : spec_.environment) {
arguments.push_back("--setenv");
arguments.push_back(env.first);
arguments.push_back(env.second);
args.push_back("--setenv");
args.push_back(env.first);
args.push_back(env.second);
}
arguments.push_back(spec_.init_command);
args.push_back(spec_.init_command);
child_ = core::posix::fork([&]() {
char **it, **pargv;
it = pargv = new char*[arguments.size() + 2];
*it = strdup("bwrap");
it++;
for (auto arg : arguments) {
*it = ::strdup(arg.c_str());
it++;
}
*it = nullptr;
if (bwrap_main(arguments.size() + 1, pargv))
return core::posix::exit::Status::failure;
return core::posix::exit::Status::success;
}, core::posix::StandardStream::empty);
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("/usr/bin/lxc-usernsexec", args, env, core::posix::StandardStream::empty);
child_group_ = child_.process_group_or_throw();
}

View file

@ -56,6 +56,20 @@ public:
private:
Container(const Container::Spec &spec);
struct IdMapping {
enum class Type {
UID,
GID
};
Type type;
int hostid;
int nsid;
int range;
};
std::vector<IdMapping> read_id_mappings();
Spec spec_;
core::posix::ChildProcess child_;
core::posix::ProcessGroup child_group_;

View file

@ -16,6 +16,7 @@
*/
#include "anbox/container_connector.h"
#include "anbox/namespace_attacher.h"
#include "anbox/config.h"
#include "anbox/utils.h"
#include "anbox/logger.h"
@ -32,101 +33,36 @@
namespace fs = boost::filesystem;
namespace {
class NamespaceAttacher {
public:
enum class ns_type {
user,
pid,
uts,
mount,
ipc,
net,
};
static std::string ns_type_to_string(ns_type type) {
switch (type) {
case ns_type::user:
return "user";
case ns_type::pid:
return "pid";
case ns_type::uts:
return "uts";
case ns_type::mount:
return "mnt";
case ns_type::ipc:
return "ipc";
case ns_type::net:
return "net";
default:
break;
}
BOOST_THROW_EXCEPTION(std::runtime_error("Unknown namespace type"));
}
NamespaceAttacher(const std::vector<ns_type> &types, pid_t pid) :
pid_(pid) {
attach(types);
}
~NamespaceAttacher() {
}
private:
void attach(const std::vector<ns_type> &types) {
std::vector<int> fds;
for (const auto &type : types) {
const auto path = anbox::utils::string_format("/proc/%lu/ns/%s", pid_, ns_type_to_string(type));
if (!fs::exists(fs::path(path)))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file"));
const auto fd = ::open(path.c_str(), O_RDONLY);
if (fd < 0)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file"));
fds.push_back(fd);
}
for (const auto &fd : fds) {
if (::setns(fd, 0) == -1)
BOOST_THROW_EXCEPTION(std::runtime_error("Could not attach to namespace"));
::close(fd);
}
}
pid_t pid_;
};
}
namespace anbox {
ContainerConnector::ContainerConnector() {
ContainerConnector::ContainerConnector(int pid) :
pid_(pid) {
}
ContainerConnector::~ContainerConnector() {
}
int ContainerConnector::run(const std::string &path) {
const auto pid = std::stol(utils::read_file_if_exists_or_throw(
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<NamespaceAttacher::ns_type>{
NamespaceAttacher::ns_type::user,
NamespaceAttacher::ns_type::mount,
NamespaceAttacher::ns_type::pid,
NamespaceAttacher::ns_type::uts,
NamespaceAttacher::ns_type::ipc,
NamespaceAttacher::ns_type::net,
namespaces_ = std::make_shared<NamespaceAttacher>(std::vector<NamespaceType>{
NamespaceType::user,
NamespaceType::mount,
NamespaceType::pid,
NamespaceType::uts,
NamespaceType::ipc,
NamespaceType::net,
}, pid);
// A few things we want to preset in our env within the container shell
std::map<std::string, std::string> env = {
{ "ANDROID_ROOT", "/" }
{ "ANDROID_ROOT", "/system" },
{ "ANDROID_DATA", "/data" },
};
auto child = core::posix::exec(path, {}, env, core::posix::StandardStream::empty, [this]() {
@ -137,6 +73,9 @@ int ContainerConnector::run(const std::string &path) {
if (::chroot("/newroot") != 0)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to enter container root filesystem"));
setuid(0);
setgid(0);
chdir("/");
});

View file

@ -20,19 +20,17 @@
#include <memory>
namespace {
class NamespaceAttacher;
}
namespace anbox {
class NamespaceAttacher;
class ContainerConnector {
public:
ContainerConnector();
ContainerConnector(int pid = -1);
~ContainerConnector();
int run(const std::string &path);
private:
int pid_;
std::shared_ptr<NamespaceAttacher> namespaces_;
};
} // namespace

View file

@ -46,6 +46,7 @@ try {
}
catch(std::exception &err) {
ERROR("%s", err.what());
return EXIT_FAILURE;
}
void Daemon::ensure_data_path() {

View file

@ -17,6 +17,7 @@
#include "anbox/logger.h"
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/graphics/mir_native_window_creator.h"
#include "OpenglRender/render_api.h"
@ -26,7 +27,9 @@
namespace anbox {
namespace graphics {
GLRendererServer::GLRendererServer() {
GLRendererServer::GLRendererServer(const std::shared_ptr<InputChannel> &input_channel) :
window_creator_(std::make_shared<MirNativeWindowCreator>(input_channel)) {
// Force the host EGL/GLES libraries as translator implementation
::setenv("ANDROID_EGL_LIB", "libEGL.so.1", 1);
::setenv("ANDROID_GLESv1_LIB", "libGLESv1_CM.so.1", 1);
@ -36,6 +39,8 @@ GLRendererServer::GLRendererServer() {
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to initialize OpenGL renderer"));
setStreamMode(RENDER_API_STREAM_MODE_UNIX);
registerSubWindowHandler(window_creator_);
}
GLRendererServer::~GLRendererServer() {

View file

@ -19,13 +19,16 @@
#define ANBOX_GRAPHICS_GL_RENDERER_SERVER_H_
#include <string>
#include <memory>
namespace anbox {
class InputChannel;
namespace graphics {
class MirNativeWindowCreator;
class GLRendererServer {
public:
GLRendererServer();
GLRendererServer(const std::shared_ptr<InputChannel> &input_channel);
~GLRendererServer();
void start();
@ -34,6 +37,7 @@ public:
private:
std::string socket_path_;
std::shared_ptr<MirNativeWindowCreator> window_creator_;
};
} // namespace graphics

View file

@ -0,0 +1,122 @@
/*
* 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/graphics/mir_display_connection.h"
#include "anbox/logger.h"
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <string>
namespace {
static const MirDisplayOutput *find_active_output(
const MirDisplayConfiguration *conf)
{
const MirDisplayOutput *output = NULL;
int d;
for (d = 0; d < (int)conf->num_outputs; d++)
{
const MirDisplayOutput *out = conf->outputs + d;
if (out->used &&
out->connected &&
out->num_modes &&
out->current_mode < out->num_modes)
{
output = out;
break;
}
}
return output;
}
}
namespace anbox {
namespace graphics {
MirDisplayConnection::MirDisplayConnection() :
connection_(nullptr),
output_id_(-1),
vertical_resolution_(0),
horizontal_resolution_(0) {
auto xdg_runtime_dir = ::getenv("XDG_RUNTIME_DIR");
if (!xdg_runtime_dir)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find XDG_RUNTIME_DIR"));
std::string socket_path = xdg_runtime_dir;
socket_path += "/mir_socket";
connection_ = mir_connect_sync(socket_path.c_str(), "anbox");
if (!mir_connection_is_valid(connection_)) {
std::string msg;
msg += "Failed to connect with Mir server: ";
msg += mir_connection_get_error_message(connection_);
BOOST_THROW_EXCEPTION(std::runtime_error(msg.c_str()));
}
MirDisplayConfiguration* display_config =
mir_connection_create_display_config(connection_);
const MirDisplayOutput *output = find_active_output(display_config);
if (!output)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find active output display"));
DEBUG("Selecting output id %d", output->output_id);
output_id_ = output->output_id;
const MirDisplayMode *mode = &output->modes[output->current_mode];
vertical_resolution_ = mode->vertical_resolution;
horizontal_resolution_ = mode->horizontal_resolution;
mir_display_config_destroy(display_config);
}
MirDisplayConnection::~MirDisplayConnection() {
mir_connection_release(connection_);
}
MirConnection* MirDisplayConnection::connection() const {
return connection_;
}
MirPixelFormat MirDisplayConnection::default_pixel_format() const {
MirPixelFormat format;
unsigned int nformats;
mir_connection_get_available_surface_formats(connection_, &format, 1, &nformats);
return format;
}
EGLNativeDisplayType MirDisplayConnection::native_display() const {
return mir_connection_get_egl_native_display(connection_);
}
int MirDisplayConnection::output_id() const {
return output_id_;
}
int MirDisplayConnection::vertical_resolution() const {
return vertical_resolution_;
}
int MirDisplayConnection::horizontal_resolution() const {
return horizontal_resolution_;
}
} // namespace graphics
} // namespace anbox

View file

@ -15,8 +15,8 @@
*
*/
#ifndef MIR_SUPPORT_SHARED_STATE_H_
#define MIR_SUPPORT_SHARED_STATE_H_
#ifndef ANBOX_GRAPHICS_MIR_DISPLAY_CONNECTION_H_
#define ANBOX_GRAPHICS_MIR_DISPLAY_CONNECTION_H_
#define MIR_EGL_PLATFORM
@ -24,27 +24,31 @@
#include <EGL/egl.h>
#include <memory>
namespace anbox {
namespace graphics {
namespace mir {
namespace support {
class SharedState {
class MirDisplayConnection {
public:
static std::shared_ptr<SharedState> get();
MirDisplayConnection();
~MirDisplayConnection();
SharedState();
~SharedState();
void ensure_connection();
void release_connection();
MirPixelFormat default_pixel_format() const;
MirConnection* connection() const;
EGLNativeDisplayType native_display() const;
int output_id() const;
int vertical_resolution() const;
int horizontal_resolution() const;
private:
MirConnection *connection_;
int output_id_;
int vertical_resolution_;
int horizontal_resolution_;
};
} // namespace support
} // namespace mir
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,61 @@
/*
* 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/graphics/mir_native_window_creator.h"
#include "anbox/graphics/mir_display_connection.h"
#include "anbox/graphics/mir_window.h"
#include "anbox/input_channel.h"
#include <boost/throw_exception.hpp>
namespace anbox {
namespace graphics {
MirNativeWindowCreator::MirNativeWindowCreator(const std::shared_ptr<InputChannel> &input_channel) :
input_channel_(input_channel),
display_connection_(std::make_shared<MirDisplayConnection>()) {
input_channel_->setup(display_connection_->horizontal_resolution(),
display_connection_->vertical_resolution());
}
MirNativeWindowCreator::~MirNativeWindowCreator() {
}
EGLNativeWindowType MirNativeWindowCreator::create_window(int x, int y, int width, int height) {
(void) x;
(void) y;
(void) width;
(void) height;
if (windows_.size() > 0)
BOOST_THROW_EXCEPTION(std::runtime_error("We currently only allow a single native window"));
auto window = std::make_shared<MirWindow>(display_connection_, input_channel_);
windows_.insert({window->native_window(), window});
return window->native_window();
}
void MirNativeWindowCreator::destroy_window(EGLNativeWindowType win) {
auto iter = windows_.find(win);
if (iter == windows_.end())
return;
windows_.erase(iter);
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,51 @@
/*
* 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_GRAPHICS_MIR_NATIVE_WINDOW_CREATOR_H_
#define ANBOX_GRAPHICS_MIR_NATIVE_WINDOW_CREATOR_H_
#include "external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h"
#include <memory>
#include <map>
namespace anbox {
class InputChannel;
namespace graphics {
class MirDisplayConnection;
class MirWindow;
class MirNativeWindowCreator : public SubWindowHandler {
public:
MirNativeWindowCreator(const std::shared_ptr<InputChannel> &input_channel);
virtual ~MirNativeWindowCreator();
EGLNativeWindowType create_window(int x, int y, int width, int height) override;
void destroy_window(EGLNativeWindowType win) override;
private:
std::shared_ptr<InputChannel> input_channel_;
std::shared_ptr<MirDisplayConnection> display_connection_;
std::map<EGLNativeWindowType,std::shared_ptr<MirWindow>> windows_;
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,107 @@
/*
* 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/graphics/mir_window.h"
#include "anbox/graphics/mir_display_connection.h"
#include "anbox/logger.h"
#include "anbox/input_channel.h"
namespace anbox {
namespace graphics {
MirWindow::MirWindow(const std::shared_ptr<MirDisplayConnection> &display, const std::shared_ptr<InputChannel> &input_channel) :
input_channel_(input_channel),
native_window_(0),
surface_(nullptr) {
const auto pixel_format = display->default_pixel_format();
DEBUG("Selected pixel format %i", pixel_format);
auto spec = mir_connection_create_spec_for_normal_surface(
display->connection(),
display->vertical_resolution(),
display->horizontal_resolution(),
pixel_format);
mir_surface_spec_set_name(spec, "anbox");
mir_surface_spec_set_event_handler(spec, handle_surface_event, this);
mir_surface_spec_set_fullscreen_on_output(spec, display->output_id());
mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware);
surface_ = mir_surface_create_sync(spec);
mir_surface_spec_release(spec);
if (!mir_surface_is_valid(surface_)) {
DEBUG("surface error: %s", mir_surface_get_error_message(surface_));
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create a Mir surface"));
}
MirSurfaceParameters parameters;
mir_surface_get_parameters(surface_, &parameters);
DEBUG("width %i height %i output id %i",
parameters.width,
parameters.height,
parameters.output_id);
auto surface_buffer_stream = mir_surface_get_buffer_stream(surface_);
native_window_ = reinterpret_cast<EGLNativeWindowType>(
mir_buffer_stream_get_egl_native_window(surface_buffer_stream));
}
MirWindow::~MirWindow() {
}
void MirWindow::handle_touch_event(MirTouchEvent const* touch_event) {
const auto action = mir_touch_event_action(touch_event, 0);
}
void MirWindow::handle_input_event(MirInputEvent const* input_event) {
const auto type = mir_input_event_get_type(input_event);
MirTouchEvent const* touch_event = nullptr;
MirKeyboardEvent const* key_event = nullptr;
switch (type) {
case mir_input_event_type_touch:
touch_event = mir_input_event_get_touch_event(input_event);
handle_touch_event(touch_event);
break;
case mir_input_event_type_pointer:
break;
case mir_input_event_type_key:
key_event = mir_input_event_get_keyboard_event(input_event);
break;
}
}
void MirWindow::handle_surface_event(MirSurface *surface, MirEvent const* event, void *context) {
const auto event_type = mir_event_get_type(event);
auto thiz = static_cast<MirWindow*>(context);
switch (event_type) {
case mir_event_type_input:
thiz->handle_input_event(mir_event_get_input_event(event));
break;
default:
break;
}
}
EGLNativeWindowType MirWindow::native_window() const {
return native_window_;
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,56 @@
/*
* 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_GRAPHICS_MIR_WINDOW_H_
#define ANBOX_GRAPHICS_MIR_WINDOW_H_
#define MIR_EGL_PLATFORM
#include <mirclient/mir_toolkit/mir_client_library.h>
#include <EGL/egl.h>
#include <memory>
namespace anbox {
class InputChannel;
namespace graphics {
class MirDisplayConnection;
class MirWindow {
public:
MirWindow(const std::shared_ptr<MirDisplayConnection> &display, const std::shared_ptr<InputChannel> &input_channel);
~MirWindow();
EGLNativeWindowType native_window() const;
private:
static void handle_surface_event(MirSurface *surface, MirEvent const* event, void *context);
void handle_input_event(MirInputEvent const* input_event);
void handle_touch_event(MirTouchEvent const* touch_event);
std::shared_ptr<InputChannel> input_channel_;
EGLNativeWindowType native_window_;
MirSurface *surface_;
};
} // namespace graphics
} // namespace anbox
#endif

282
src/anbox/id_map_writer.cpp Normal file
View file

@ -0,0 +1,282 @@
/*
* 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/id_map_writer.h"
#include "anbox/not_reachable.h"
#include "anbox/utils.h"
#include <fstream>
#include <sstream>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <pwd.h>
namespace fs = boost::filesystem;
#define getid(type) ((unsigned) ((type) == GID ? getgid() : getuid()))
#define idfile(type) ((type) == GID ? "gid_map" : "uid_map")
#define idname(type) ((type) == GID ? "GID" : "UID")
#define subpath(type) ((type) == GID ? "/etc/subgid" : "/etc/subuid")
namespace {
char *append(char **destination, const char *format, ...) {
char *extra, *result;
va_list args;
va_start(args, format);
if (vasprintf(&extra, format, args) < 0)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
va_end(args);
if (*destination == NULL) {
*destination = extra;
return extra;
}
if (asprintf(&result, "%s%s", *destination, extra) < 0)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
free(*destination);
free(extra);
*destination = result;
return result;
}
char *string(const char *format, ...) {
char *result;
va_list args;
va_start(args, format);
if (vasprintf(&result, format, args) < 0)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
va_end(args);
return result;
}
static char *getmap(pid_t pid, int type) {
char *line = NULL, *result = NULL, *path;
size_t size;
unsigned count, first, lower;
FILE *file;
if (pid == -1)
path = string("/proc/self/%s", idfile(type));
else
path = string("/proc/%d/%s", pid, idfile(type));
if (!(file = fopen(path, "r")))
BOOST_THROW_EXCEPTION(std::runtime_error(""));
while (getline(&line, &size, file) >= 0) {
if (sscanf(line, " %u %u %u", &first, &lower, &count) != 3)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
append(&result, "%s%u:%u:%u", result ? "," : "", first, lower, count);
}
if (!result)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
fclose(file);
free(line);
free(path);
return result;
}
static char *mapitem(char *map, unsigned *first, unsigned *lower,
unsigned *count) {
ssize_t skip;
while (map && *map && strchr(",;", *map))
map++;
if (map == NULL || *map == '\0')
return NULL;
if (sscanf(map, "%u:%u:%u%zn", first, lower, count, &skip) < 3)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
return map + skip;
}
static char *rangeitem(char *range, unsigned *start, unsigned *length) {
ssize_t skip;
while (range && *range && strchr(",;", *range))
range++;
if (range == NULL || *range == '\0')
return NULL;
if (sscanf(range, "%u:%u%zn", start, length, &skip) < 2)
BOOST_THROW_EXCEPTION(std::runtime_error(""));
return range + skip;
}
static char *readranges(int type) {
char *line = NULL, *range, *user;
size_t end, size;
struct passwd *passwd;
unsigned length, start;
FILE *file;
range = string("%u:1", getid(type));
if (!(file = fopen(subpath(type), "r")))
return range;
user = getenv("USER");
user = user ? user : getenv("LOGNAME");
user = user ? user : getlogin();
if (!user || !(passwd = getpwnam(user)) || passwd->pw_uid != getuid()) {
if (!(passwd = getpwuid(getuid())))
BOOST_THROW_EXCEPTION(std::runtime_error(""));
user = passwd->pw_name;
}
endpwent();
while (getline(&line, &size, file) >= 0) {
if (strncmp(line, user, strlen(user)))
continue;
if (sscanf(line + strlen(user), ":%u:%u%zn", &start, &length, &end) < 2)
continue;
if (strchr(":\n", line[end + strlen(user) + 1]))
append(&range, ",%u:%u", start, length);
}
free(line);
fclose(file);
return range;
}
static char *rootdefault(int type) {
char *cursor, *map, *result;
unsigned count, first, last = INVALID, lower;
cursor = map = getmap(-1, type);
while ((cursor = mapitem(cursor, &first, &lower, &count)))
if (last == INVALID || last < first + count - 1)
last = first + count - 1;
result = string("0:%u:1", last);
cursor = map;
while ((cursor = mapitem(cursor, &first, &lower, &count))) {
if (first == 0) {
if (count == 1 && first >= last)
error(1, 0, "No unprivileged %s available\n", idname(type));
first++, lower++, count--;
}
if (last <= first + count - 1 && count > 0)
count--;
if (count > 0)
append(&result, "%s%u:%u:%u", result ? "," : "", first, first, count);
}
free(map);
return result;
}
static char *userdefault(int type) {
char *cursor, *map, *range, *result = NULL;
unsigned count, first, index = 0, length, lower, start;
if (geteuid() != 0)
return string("0:%u:1", getid(type));
map = getmap(-1, type);
range = readranges(type);
while ((range = rangeitem(range, &start, &length))) {
cursor = map;
while ((cursor = mapitem(cursor, &first, &lower, &count))) {
if (start + length <= first || first + count <= start)
continue;
if (first + count < start + length)
length = start - first + count;
if (start < first) {
index += first - start;
length -= first - start;
start = first;
}
append(&result, "%s%u:%u:%u", result ? "," : "", index, start, length);
index += length;
}
}
free(map);
free(range);
return result;
}
static void validate(char *range, unsigned first, unsigned count) {
unsigned length, start;
while ((range = rangeitem(range, &start, &length)))
if (first < start + length && start < first + count) {
if (first < start)
validate(range, first, start - first);
if (first + count > start + length)
validate(range, start + length, first + count - start - length);
return;
}
error(1, 0, "Cannot map onto IDs that are not delegated to you");
}
static void verifymap(char *map, char *range) {
unsigned count, first, lower;
while ((map = mapitem(map, &first, &lower, &count)))
validate(range, lower, count);
}
static void writemap(pid_t pid, int type, char *map) {
char *path, *range, *text = NULL;
int fd;
unsigned count, first, lower;
if (!map) {
map = (getuid() == 0 ? rootdefault : userdefault)(type);
} else if (getuid() != 0) {
range = readranges(type);
verifymap(map, range);
free(range);
}
while ((map = mapitem(map, &first, &lower, &count)))
append(&text, "%u %u %u\n", first, lower, count);
path = string("/proc/%d/%s", pid, idfile(type));
if ((fd = open(path, O_WRONLY)) < 0)
error(1, 0, "Failed to set container %s map", idname(type));
else if (write(fd, text, strlen(text)) != (ssize_t) strlen(text))
error(1, 0, "Failed to set container %s map", idname(type));
close(fd);
free(path);
free(text);
}
}
namespace anbox {
IdMapWriter::IdMapWriter(const Type &type, const pid_t &pid) :
type_(type),
pid_(pid) {
}
IdMapWriter::~IdMapWriter() {
}
void IdMapWriter::apply() {
const auto map = retrieve_mappings();
}
} // namespace anbox

43
src/anbox/id_map_writer.h Normal file
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_ID_MAP_WRITER_H_
#define ANBOX_ID_MAP_WRITER_H_
#include <vector>
#include <boost/filesystem.hpp>
namespace anbox {
class IdMapWriter {
public:
enum class Type {
UID,
GID
};
IdMapWriter(const Type &type, const pid_t &pid = -1);
~IdMapWriter();
void apply();
private:
Type type_;
pid_t pid_;
};
} // namespace anbox
#endif

View file

@ -0,0 +1,75 @@
/*
* 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/input_channel.h"
#include "anbox/logger.h"
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
namespace anbox {
InputChannel::InputChannel() :
dev_(nullptr) {
}
InputChannel::~InputChannel() {
if (dev_)
libevdev_uinput_destroy(dev_);
}
void InputChannel::setup(int width, int height) {
DEBUG("");
auto dev = libevdev_new();
libevdev_set_name(dev, "anbox-tp");
libevdev_enable_event_type(dev, EV_ABS);
struct input_absinfo mt_tracking_id = { 0, 0, 255, 0, 0, 0 };
libevdev_enable_event_code(dev, EV_ABS, ABS_MT_TRACKING_ID, &mt_tracking_id);
struct input_absinfo mt_slot = { 0, 0, 255, 0, 0, 0 };
libevdev_enable_event_code(dev, EV_ABS, ABS_MT_SLOT, &mt_slot);
struct input_absinfo mt_pos_x = { 0, 0, width, 0, 0, 0 };
libevdev_enable_event_code(dev, EV_ABS, ABS_MT_POSITION_X, &mt_pos_x);
struct input_absinfo mt_pos_y = { 0, 0, height, 0, 0, 0 };
libevdev_enable_event_code(dev, EV_ABS, ABS_MT_POSITION_Y, &mt_pos_y);
struct input_absinfo mt_pressure = { 0, 0, 5, 0, 0, 0 };
libevdev_enable_event_code(dev, EV_ABS, ABS_MT_PRESSURE, &mt_pressure);
libevdev_enable_event_type(dev, EV_SYN);
libevdev_enable_property(dev, INPUT_PROP_DIRECT);
if (libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &dev_) < 0) {
libevdev_free(dev);
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create uinput based device"));
}
}
void InputChannel::push_event(const Event &event) {
libevdev_uinput_write_event(dev_, event.type, event.code, event.value);
}
std::string InputChannel::dev_path() const {
if (!dev_)
return "";
return std::string(libevdev_uinput_get_devnode(dev_));
}
} // namespace anbox

47
src/anbox/input_channel.h Normal file
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_INPUT_CHANNEL_H_
#define ANBOX_INPUT_CHANNEL_H_
#include <string>
struct libevdev_uinput;
namespace anbox {
class InputChannel {
public:
InputChannel();
~InputChannel();
struct Event {
int type;
int code;
int value;
};
void setup(int width, int height);
void push_event(const Event &event);
std::string dev_path() const;
private:
struct libevdev_uinput *dev_;
};
} // namespace anbox
#endif

View file

@ -0,0 +1,81 @@
/*
* 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/namespace_attacher.h"
#include "anbox/utils.h"
#include <boost/throw_exception.hpp>
#include <boost/filesystem.hpp>
#include <unistd.h>
#include <fcntl.h>
namespace fs = boost::filesystem;
namespace anbox {
std::string NamespaceAttacher::ns_type_to_string(NamespaceType type) {
switch (type) {
case NamespaceType::user:
return "user";
case NamespaceType::pid:
return "pid";
case NamespaceType::uts:
return "uts";
case NamespaceType::mount:
return "mnt";
case NamespaceType::ipc:
return "ipc";
case NamespaceType::net:
return "net";
default:
break;
}
BOOST_THROW_EXCEPTION(std::runtime_error("Unknown namespace type"));
}
NamespaceAttacher::NamespaceAttacher(const std::vector<NamespaceType> &types, pid_t pid) :
pid_(pid) {
attach(types);
}
NamespaceAttacher::~NamespaceAttacher() {
}
void NamespaceAttacher::attach(const std::vector<NamespaceType> &types) {
std::vector<int> fds;
for (const auto &type : types) {
const auto path = anbox::utils::string_format("/proc/%lu/ns/%s", pid_, ns_type_to_string(type));
if (!fs::exists(fs::path(path)))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file"));
const auto fd = ::open(path.c_str(), O_RDONLY);
if (fd < 0)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file"));
fds.push_back(fd);
}
for (const auto &fd : fds) {
if (::setns(fd, 0) == -1)
BOOST_THROW_EXCEPTION(std::runtime_error("Could not attach to namespace"));
::close(fd);
}
}
} // namespace anbox

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/>.
*
*/
#ifndef ANBOX_NAMESPACE_ATTACHER_H_
#define ANBOX_NAMESPACE_ATTACHER_H_
#include <memory>
#include <vector>
#include <string>
namespace anbox {
enum class NamespaceType {
user,
pid,
uts,
mount,
ipc,
net,
};
class NamespaceAttacher {
public:
static std::string ns_type_to_string(NamespaceType type);
NamespaceAttacher(const std::vector<NamespaceType> &types, pid_t pid);
~NamespaceAttacher();
private:
void attach(const std::vector<NamespaceType> &types);
pid_t pid_;
};
} // namespace
#endif

View file

@ -23,6 +23,10 @@
#include "anbox/graphics/opengles_message_processor.h"
#include "anbox/support/boot_properties_message_processor.h"
#include "anbox/support/null_message_processor.h"
#include "anbox/support/hwcontrol_message_processor.h"
#include "anbox/support/sensors_message_processor.h"
#include "anbox/support/camera_message_processor.h"
#include "anbox/support/fingerprint_message_processor.h"
namespace ba = boost::asio;
@ -81,6 +85,14 @@ QemuPipeConnectionCreator::client_type QemuPipeConnectionCreator::identify_clien
// take this as a own service instance as that is what it is.
else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:boot-properties"))
return client_type::qemud_boot_properties;
else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:hw-control"))
return client_type::qemud_hw_control;
else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:sensors"))
return client_type::qemud_sensors;
else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:camera"))
return client_type::qemud_camera;
else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:fingerprintlisten"))
return client_type::qemud_fingerprint;
return client_type::invalid;
}
@ -90,6 +102,14 @@ std::shared_ptr<MessageProcessor> QemuPipeConnectionCreator::create_processor(co
return std::make_shared<graphics::OpenGlesMessageProcessor>(renderer_socket_path_, runtime_, messenger);
else if (type == client_type::qemud_boot_properties)
return std::make_shared<support::BootPropertiesMessageProcessor>(messenger);
else if (type == client_type::qemud_hw_control)
return std::make_shared<support::HwControlMessageProcessor>(messenger);
else if (type == client_type::qemud_sensors)
return std::make_shared<support::SensorsMessageProcessor>(messenger);
else if (type == client_type::qemud_camera)
return std::make_shared<support::CameraMessageProcessor>(messenger);
else if (type == client_type::qemud_fingerprint)
return std::make_shared<support::FingerprintMessageProcessor>(messenger);
return std::make_shared<support::NullMessageProcessor>();
}

View file

@ -46,6 +46,10 @@ public:
invalid,
opengles,
qemud_boot_properties,
qemud_hw_control,
qemud_sensors,
qemud_camera,
qemud_fingerprint,
};
private:

View file

@ -51,14 +51,22 @@ void SocketMessenger::send(char const* data, size_t length)
VariableLengthArray<serialization_buffer_size> whole_message{length};
std::copy(data, data + length, whole_message.data());
try {
std::unique_lock<std::mutex> lg(message_lock);
ba::write(*socket,
ba::buffer(whole_message.data(), whole_message.size()),
boost::asio::transfer_all());
}
catch (std::exception &err) {
ERROR("Failed to write message: %s", err.what());
while (true) {
try {
std::unique_lock<std::mutex> lg(message_lock);
ba::write(*socket,
ba::buffer(whole_message.data(), whole_message.size()),
boost::asio::transfer_all());
}
catch (const boost::system::system_error &err) {
if (err.code() == boost::asio::error::try_again)
continue;
}
catch (const std::exception &err) {
ERROR("Could not write message: %s", err.what());
}
break;
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2016 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Thomas Voß <thomas.voss@canonical.com>
*
*/
#include "anbox/not_reachable.h"
#include <boost/format.hpp>
anbox::util::NotReachable::NotReachable(const std::string& function, const std::string& file, std::uint32_t line)
: std::logic_error{(boost::format{"Code should not be reachable: %1% in %2%:%3%"} % function % file % line).str()}
{
}
void anbox::util::not_reachable(const std::string& function, const std::string& file, std::uint32_t line)
{
throw NotReachable{function, file, line};
}

40
src/anbox/not_reachable.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2016 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Thomas Voß <thomas.voss@canonical.com>
*
*/
#ifndef ANBOX_UTIL_NOT_REACHABLE_H_
#define ANBOX_UTIL_NOT_REACHABLE_H_
#include <stdexcept>
#include <string>
namespace anbox {
namespace util {
/// @brief NotReachable is thrown from not_reachable.
struct NotReachable : public std::logic_error
{
/// @brief NotImplemented initializes a new instance for the given function name.
NotReachable(const std::string& function, const std::string& file, std::uint32_t line);
};
/// @brief not_reachable throws NotReachable.
[[noreturn]] void not_reachable(const std::string& function, const std::string& file, std::uint32_t line);
} // namespace util
} // namespace anbox
#endif

View file

@ -38,7 +38,7 @@ void exception_safe_run(boost::asio::io_service& service) {
break;
}
catch (const std::exception& e) {
std::cerr << "test" << e.what();
std::cerr << e.what() << std::endl;
}
catch (...) {
std::cerr << "Unknown exception caught while executing boost::asio::io_service";

View file

@ -15,67 +15,24 @@
*
*/
#include "anbox/logger.h"
#include "anbox/support/boot_properties_message_processor.h"
#include "anbox/network/socket_messenger.h"
#include "anbox/network/connections.h"
#include "anbox/network/delegate_message_processor.h"
#include <string.h>
namespace ba = boost::asio;
namespace {
static constexpr const long header_size{4};
}
#include "anbox/logger.h"
namespace anbox {
namespace support {
BootPropertiesMessageProcessor::BootPropertiesMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
messenger_(messenger) {
QemudMessageProcessor(messenger) {
}
BootPropertiesMessageProcessor::~BootPropertiesMessageProcessor() {
}
bool BootPropertiesMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
for (const auto &byte : data)
buffer_.push_back(byte);
process_commands();
return true;
}
void BootPropertiesMessageProcessor::process_commands() {
while (true) {
if (buffer_.size() < header_size)
break;
char header[header_size] = { 0 };
::memcpy(header, buffer_.data(), header_size);
unsigned int body_size = 0;
::sscanf(header, "%04x", &body_size);
if (body_size != buffer_.size() - header_size)
break;
std::string command;
// Make sure we only copy as much bytes as we have to and not more
command.insert(0, reinterpret_cast<const char*>(buffer_.data()) + header_size, body_size);
if (command == "list")
list_properties();
else
DEBUG("Unknown command '%s'", command);
const auto consumed = header_size + body_size;
buffer_.erase(buffer_.begin(), buffer_.begin() + consumed);
const auto remaining = buffer_.size() - consumed;
if (remaining <= 0)
break;
}
void BootPropertiesMessageProcessor::handle_command(const std::string &command) {
if (command == "list")
list_properties();
else
DEBUG("Unknown command '%s'", command);
}
void BootPropertiesMessageProcessor::list_properties() {
@ -96,18 +53,20 @@ void BootPropertiesMessageProcessor::list_properties() {
// TODO(morphis): Using HDPI here for now but should be adjusted to the device
// we're running on.
"qemu.sf.lcd_density=240"
"qemu.sf.lcd_density=240",
// libhwui detects that we support certain GLESv3 extensions which
// we don't yet support in our host channel so we have to disable
// those things here.
"ro.hwui.use_gpu_pixel_buffers=0",
};
for (const auto &prop : properties) {
char header[header_size + 1];
std::snprintf(header, header_size + 1, "%04x", prop.length());
messenger_->send(header, header_size);
send_header(prop.length());
messenger_->send(prop.c_str(), prop.length());
}
// Send terminating NULL byte
messenger_->send(static_cast<const char*>(""), 1);
finish_message();
}
} // namespace support
} // namespace anbox

View file

@ -18,30 +18,20 @@
#ifndef ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_
#include <memory>
#include <boost/asio.hpp>
#include "anbox/runtime.h"
#include "anbox/network/message_processor.h"
#include "anbox/network/socket_messenger.h"
#include "anbox/network/socket_connection.h"
#include "anbox/support/qemud_message_processor.h"
namespace anbox {
namespace support {
class BootPropertiesMessageProcessor : public network::MessageProcessor {
class BootPropertiesMessageProcessor : public QemudMessageProcessor {
public:
BootPropertiesMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~BootPropertiesMessageProcessor();
bool process_data(const std::vector<std::uint8_t> &data) override;
protected:
void handle_command(const std::string &command) override;
private:
void process_commands();
void list_properties();
std::shared_ptr<network::SocketMessenger> messenger_;
std::vector<std::uint8_t> buffer_;
};
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,68 @@
/*
* 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/logger.h"
#include "anbox/support/camera_message_processor.h"
namespace anbox {
namespace support {
CameraMessageProcessor::CameraMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
messenger_(messenger) {
}
CameraMessageProcessor::~CameraMessageProcessor() {
}
bool CameraMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
DEBUG("Received: %s", utils::hex_dump(data.data(), data.size()));
for (const auto &byte : data)
buffer_.push_back(byte);
process_commands();
return true;
}
void CameraMessageProcessor::process_commands() {
while (true) {
size_t size;
for (size = 0; size < buffer_.size(); size++) {
if (buffer_.at(size) == 0x0)
break;
}
std::string command;
command.insert(0, reinterpret_cast<const char*>(buffer_.data()), size);
buffer_.erase(buffer_.begin(), buffer_.begin() + size);
handle_command(command);
}
}
void CameraMessageProcessor::handle_command(const std::string &command) {
if (command == "list")
list();
}
void CameraMessageProcessor::list() {
char buf[5];
snprintf(buf, 5, "\n");
messenger_->send(buf, strlen(buf));
}
} // namespace support
} // namespace anbox

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/>.
*
*/
#ifndef ANBOX_SUPPORT_CAMERA_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_CAMERA_MESSAGE_PROCESSOR_H_
#include "anbox/network/message_processor.h"
#include "anbox/network/socket_messenger.h"
namespace anbox {
namespace support {
class CameraMessageProcessor : public network::MessageProcessor {
public:
CameraMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~CameraMessageProcessor();
bool process_data(const std::vector<std::uint8_t> &data) override;
private:
void process_commands();
void handle_command(const std::string &command);
void list();
std::shared_ptr<network::SocketMessenger> messenger_;
std::vector<std::uint8_t> buffer_;
};
} // namespace graphics
} // namespace anbox
#endif

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/>.
*
*/
#include "anbox/logger.h"
#include "anbox/support/fingerprint_message_processor.h"
namespace anbox {
namespace support {
FingerprintMessageProcessor::FingerprintMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
QemudMessageProcessor(messenger) {
}
FingerprintMessageProcessor::~FingerprintMessageProcessor() {
}
void FingerprintMessageProcessor::handle_command(const std::string &command) {
if (command == "listen")
listen();
}
void FingerprintMessageProcessor::listen() {
char buf[12];
snprintf(buf, sizeof(buf), "off");
send_header(strlen(buf));
messenger_->send(buf, strlen(buf));
finish_message();
}
} // namespace support
} // namespace anbox

View file

@ -0,0 +1,39 @@
/*
* 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_SUPPORT_FINGERPRINT_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_FINGERPRINT_MESSAGE_PROCESSOR_H_
#include "anbox/support/qemud_message_processor.h"
namespace anbox {
namespace support {
class FingerprintMessageProcessor : public QemudMessageProcessor {
public:
FingerprintMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~FingerprintMessageProcessor();
protected:
void handle_command(const std::string &command) override;
private:
void listen();
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -15,68 +15,27 @@
*
*/
#include "anbox/logger.h"
#include "anbox/support/hwcontrol_message_processor.h"
#include "anbox/network/socket_messenger.h"
#include "anbox/network/connections.h"
#include <string.h>
namespace ba = boost::asio;
namespace {
static constexpr const long header_size{4};
}
#include "anbox/logger.h"
namespace anbox {
namespace support {
HwControlMessageProcessor::HwControlMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
messenger_(messenger) {
QemudMessageProcessor(messenger) {
}
HwControlMessageProcessor::~HwControlMessageProcessor() {
}
bool HwControlMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
for (const auto &byte : data)
buffer_.push_back(byte);
process_commands();
return true;
}
void HwControlMessageProcessor::process_commands() {
while (true) {
if (buffer_.size() < header_size)
break;
char header[header_size] = { 0 };
::memcpy(header, buffer_.data(), header_size);
unsigned int body_size = 0;
::sscanf(header, "%04x", &body_size);
if (body_size != buffer_.size() - header_size)
break;
std::string command;
// Make sure we only copy as much bytes as we have to and not more
command.insert(0, reinterpret_cast<const char*>(buffer_.data()) + header_size, body_size);
if (command == "power:screen_state:wake")
DEBUG("Got screen wake command");
else if (command == "power:screen_state:standby")
DEBUG("Got screen standby command");
else
DEBUG("Unknown command '%s'", command);
const auto consumed = header_size + body_size;
buffer_.erase(buffer_.begin(), buffer_.begin() + consumed);
const auto remaining = buffer_.size() - consumed;
if (remaining <= 0)
break;
}
void HwControlMessageProcessor::handle_command(const std::string &command) {
if (command == "power:screen_state:wake")
DEBUG("Got screen wake command");
else if (command == "power:screen_state:standby")
DEBUG("Got screen standby command");
else if (utils::string_starts_with(command, "power:light:brightness:lcd_backlight"))
DEBUG("Got LCD backligh brightness control command");
else
DEBUG("Unknown command '%s'", command);
}
} // namespace support
} // namespace anbox

View file

@ -18,29 +18,17 @@
#ifndef ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_
#include <memory>
#include <boost/asio.hpp>
#include "anbox/runtime.h"
#include "anbox/network/message_processor.h"
#include "anbox/network/socket_messenger.h"
#include "anbox/network/socket_connection.h"
#include "anbox/support/qemud_message_processor.h"
namespace anbox {
namespace support {
class HwControlMessageProcessor : public network::MessageProcessor {
class HwControlMessageProcessor : public QemudMessageProcessor {
public:
HwControlMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~HwControlMessageProcessor();
bool process_data(const std::vector<std::uint8_t> &data) override;
private:
void process_commands();
std::shared_ptr<network::SocketMessenger> messenger_;
std::vector<std::uint8_t> buffer_;
protected:
void handle_command(const std::string &command) override;
};
} // namespace graphics
} // namespace anbox

View file

@ -16,6 +16,8 @@
*/
#include "anbox/support/null_message_processor.h"
#include "anbox/utils.h"
#include "anbox/logger.h"
#include <string.h>
@ -27,7 +29,8 @@ NullMessageProcessor::NullMessageProcessor() {
NullMessageProcessor::~NullMessageProcessor() {
}
bool NullMessageProcessor::process_data(const std::vector<std::uint8_t>&) {
bool NullMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
DEBUG("Received: %s", utils::hex_dump(data.data(), data.size()));
return true;
}
} // namespace support

View file

@ -18,11 +18,6 @@
#ifndef ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_
#include <memory>
#include <boost/asio.hpp>
#include "anbox/runtime.h"
#include "anbox/network/message_processor.h"
namespace anbox {

View file

@ -0,0 +1,87 @@
/*
* 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/support/qemud_message_processor.h"
#include "anbox/utils.h"
#include "anbox/logger.h"
#include <string.h>
namespace {
static constexpr const long header_size{4};
}
namespace anbox {
namespace support {
QemudMessageProcessor::QemudMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
messenger_(messenger) {
}
QemudMessageProcessor::~QemudMessageProcessor() {
}
bool QemudMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
DEBUG("Received: %s", utils::hex_dump(data.data(), data.size()));
for (const auto &byte : data)
buffer_.push_back(byte);
process_commands();
return true;
}
void QemudMessageProcessor::process_commands() {
while (true) {
if (buffer_.size() < header_size)
break;
char header[header_size] = { 0 };
::memcpy(header, buffer_.data(), header_size);
unsigned int body_size = 0;
::sscanf(header, "%04x", &body_size);
if (body_size != buffer_.size() - header_size)
break;
std::string command;
// Make sure we only copy as much bytes as we have to and not more
command.insert(0, reinterpret_cast<const char*>(buffer_.data()) + header_size, body_size);
handle_command(command);
const auto consumed = header_size + body_size;
buffer_.erase(buffer_.begin(), buffer_.begin() + consumed);
const auto remaining = buffer_.size() - consumed;
if (remaining <= 0)
break;
}
}
void QemudMessageProcessor::send_header(const size_t &size) {
char header[header_size + 1];
std::snprintf(header, header_size + 1, "%04x", size);
messenger_->send(header, header_size);
}
void QemudMessageProcessor::finish_message() {
// Send terminating NULL byte
messenger_->send(static_cast<const char*>(""), 1);
}
} // namespace support
} // namespace anbox

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/>.
*
*/
#ifndef ANBOX_SUPPORT_QEMUD_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_QEMUD_MESSAGE_PROCESSOR_H_
#include "anbox/network/message_processor.h"
#include "anbox/network/socket_messenger.h"
namespace anbox {
namespace support {
class QemudMessageProcessor : public network::MessageProcessor {
public:
QemudMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~QemudMessageProcessor();
bool process_data(const std::vector<std::uint8_t> &data) override;
protected:
virtual void handle_command(const std::string &command) = 0;
void send_header(const size_t &size);
void finish_message();
std::shared_ptr<network::SocketMessenger> messenger_;
private:
void process_commands();
std::vector<std::uint8_t> buffer_;
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,46 @@
/*
* 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/logger.h"
#include "anbox/support/sensors_message_processor.h"
namespace anbox {
namespace support {
SensorsMessageProcessor::SensorsMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger) :
QemudMessageProcessor(messenger) {
}
SensorsMessageProcessor::~SensorsMessageProcessor() {
}
void SensorsMessageProcessor::handle_command(const std::string &command) {
DEBUG("command %s", command);
if (command == "list-sensors")
list_sensors();
}
void SensorsMessageProcessor::list_sensors() {
// We don't support sensors yet so we mark all as disabled
int mask = 0;
char buf[12];
snprintf(buf, sizeof(buf), "%d", mask);
send_header(strlen(buf));
messenger_->send(buf, strlen(buf));
finish_message();
}
} // namespace support
} // namespace anbox

View file

@ -0,0 +1,39 @@
/*
* 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_SUPPORT_SENSORS_MESSAGE_PROCESSOR_H_
#define ANBOX_SUPPORT_SENSORS_MESSAGE_PROCESSOR_H_
#include "anbox/support/qemud_message_processor.h"
namespace anbox {
namespace support {
class SensorsMessageProcessor : public QemudMessageProcessor {
public:
SensorsMessageProcessor(const std::shared_ptr<network::SocketMessenger> &messenger);
~SensorsMessageProcessor();
protected:
void handle_command(const std::string &command) override;
private:
void list_sensors();
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -132,5 +132,12 @@ std::string hex_dump(const uint8_t *data, uint32_t size) {
return buffer.str();
}
std::string get_env_value(const std::string &name, const std::string &default_value) {
char *value = getenv(name.c_str());
if (!value)
return default_value;
return std::string(value);
}
} // namespace utils
} // namespace anbox

View file

@ -38,6 +38,8 @@ bool string_starts_with(const std::string &text, const std::string &prefix);
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 = "");
template<typename... Types>
static std::string string_format(const std::string& fmt_str, Types&&... args);
} // namespace utils

22
src/container_main.cpp Normal file
View file

@ -0,0 +1,22 @@
/*
* 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/>.
*
*/
extern "C" int bwrap_main(int argc, char **argv);
int main(int argc, char **argv) {
return bwrap_main(argc, argv);
}