diff --git a/CMakeLists.txt b/CMakeLists.txt index 33960f8..2aaf834 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/external/android-emugl/host/libs/CMakeLists.txt b/external/android-emugl/host/libs/CMakeLists.txt index 3a509f4..bb38eb0 100644 --- a/external/android-emugl/host/libs/CMakeLists.txt +++ b/external/android-emugl/host/libs/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(mir_support) add_subdirectory(GLESv1_dec) add_subdirectory(GLESv2_dec) add_subdirectory(libOpenGLESDispatch) diff --git a/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt index 52edee2..3377489 100644 --- a/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt @@ -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) diff --git a/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt index 08e3071..34a700e 100644 --- a/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt @@ -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) diff --git a/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt b/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt index 27846a1..7a25973 100644 --- a/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt +++ b/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt @@ -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}) diff --git a/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp index a85f0d5..d7f3f06 100644 --- a/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp +++ b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp @@ -28,10 +28,6 @@ #include -#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); diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h index 2c1402a..49af52b 100644 --- a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h @@ -20,10 +20,21 @@ #include +#include + #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 &handler); + typedef void (*SubWindowRepaintCallback)(void*); // Create a new sub-window that will be used to display the content of the diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_delegate.cpp b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_delegate.cpp new file mode 100644 index 0000000..8984d2f --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_delegate.cpp @@ -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 +#include +#include + +namespace { +static std::shared_ptr current_handler = nullptr; +} + +void registerSubWindowHandler(const std::shared_ptr &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; +} diff --git a/external/android-emugl/host/libs/mir_support/CMakeLists.txt b/external/android-emugl/host/libs/mir_support/CMakeLists.txt deleted file mode 100644 index ed29cd9..0000000 --- a/external/android-emugl/host/libs/mir_support/CMakeLists.txt +++ /dev/null @@ -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}) diff --git a/external/android-emugl/host/libs/mir_support/shared_state.cpp b/external/android-emugl/host/libs/mir_support/shared_state.cpp deleted file mode 100644 index 3f4ad1f..0000000 --- a/external/android-emugl/host/libs/mir_support/shared_state.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 Simon Fels - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - */ - -#include "mir_support/shared_state.h" - -namespace mir { -namespace support { - -std::shared_ptr SharedState::get() { - static auto instance = std::make_shared(); - 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 diff --git a/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt index dad6019..5bc3558 100644 --- a/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt @@ -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) diff --git a/external/bubblewrap/bubblewrap.c b/external/bubblewrap/bubblewrap.c index da737f7..1c0feee 100644 --- a/external/bubblewrap/bubblewrap.c +++ b/external/bubblewrap/bubblewrap.c @@ -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 = "/"; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e1b77d4..3f13fd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp index f6f9a0f..473ea72 100644 --- a/src/anbox/cmds/run.cpp +++ b/src/anbox/cmds/run.cpp @@ -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 @@ -66,7 +67,9 @@ anbox::cmds::Run::Run() auto rt = Runtime::create(); - auto renderer = std::make_shared(); + auto input_channel = std::make_shared(); + + auto renderer = std::make_shared(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); diff --git a/src/anbox/cmds/shell.cpp b/src/anbox/cmds/shell.cpp index 646a25c..20fd33d 100644 --- a/src/anbox/cmds/shell.cpp +++ b/src/anbox/cmds/shell.cpp @@ -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"); }); } diff --git a/src/anbox/cmds/shell.h b/src/anbox/cmds/shell.h index 24e0b58..daa9969 100644 --- a/src/anbox/cmds/shell.h +++ b/src/anbox/cmds/shell.h @@ -31,7 +31,7 @@ public: Shell(); private: - std::string rootfs_; + int pid_; }; } // namespace cmds } // namespace anbox diff --git a/src/anbox/container.cpp b/src/anbox/container.cpp index d7bb4fc..6440101 100644 --- a/src/anbox/container.cpp +++ b/src/anbox/container.cpp @@ -19,6 +19,9 @@ #include +#include +#include + #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 +#include 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::read_id_mappings() { + std::vector 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 arguments = { - "--ro-bind", spec_.rootfs_path, "/", + DEBUG("uid %d gid %d", getuid(), getgid()); + std::vector args = { + // We need to setup user mapping here as lxc-usernsexec will not + // map our current user to root which we need to allow our container + // to access files we've created. + "-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 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(); } diff --git a/src/anbox/container.h b/src/anbox/container.h index 8e12134..55dddd7 100644 --- a/src/anbox/container.h +++ b/src/anbox/container.h @@ -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 read_id_mappings(); + Spec spec_; core::posix::ChildProcess child_; core::posix::ProcessGroup child_group_; diff --git a/src/anbox/container_connector.cpp b/src/anbox/container_connector.cpp index 99f9a93..2d832c5 100644 --- a/src/anbox/container_connector.cpp +++ b/src/anbox/container_connector.cpp @@ -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 &types, pid_t pid) : - pid_(pid) { - - attach(types); - } - - ~NamespaceAttacher() { - } - -private: - void attach(const std::vector &types) { - std::vector 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(std::vector{ - 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(std::vector{ + 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 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("/"); }); diff --git a/src/anbox/container_connector.h b/src/anbox/container_connector.h index 9bed3ef..10a7d10 100644 --- a/src/anbox/container_connector.h +++ b/src/anbox/container_connector.h @@ -20,19 +20,17 @@ #include -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 namespaces_; }; } // namespace diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp index 6966cb1..02b1c34 100644 --- a/src/anbox/daemon.cpp +++ b/src/anbox/daemon.cpp @@ -46,6 +46,7 @@ try { } catch(std::exception &err) { ERROR("%s", err.what()); + return EXIT_FAILURE; } void Daemon::ensure_data_path() { diff --git a/src/anbox/graphics/gl_renderer_server.cpp b/src/anbox/graphics/gl_renderer_server.cpp index f37908b..41ae098 100644 --- a/src/anbox/graphics/gl_renderer_server.cpp +++ b/src/anbox/graphics/gl_renderer_server.cpp @@ -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 &input_channel) : + window_creator_(std::make_shared(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() { diff --git a/src/anbox/graphics/gl_renderer_server.h b/src/anbox/graphics/gl_renderer_server.h index 8eb1108..795f98a 100644 --- a/src/anbox/graphics/gl_renderer_server.h +++ b/src/anbox/graphics/gl_renderer_server.h @@ -19,13 +19,16 @@ #define ANBOX_GRAPHICS_GL_RENDERER_SERVER_H_ #include +#include namespace anbox { +class InputChannel; namespace graphics { +class MirNativeWindowCreator; class GLRendererServer { public: - GLRendererServer(); + GLRendererServer(const std::shared_ptr &input_channel); ~GLRendererServer(); void start(); @@ -34,6 +37,7 @@ public: private: std::string socket_path_; + std::shared_ptr window_creator_; }; } // namespace graphics diff --git a/src/anbox/graphics/mir_display_connection.cpp b/src/anbox/graphics/mir_display_connection.cpp new file mode 100644 index 0000000..9ac1008 --- /dev/null +++ b/src/anbox/graphics/mir_display_connection.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ +#include "anbox/graphics/mir_display_connection.h" +#include "anbox/logger.h" + +#include + +#include +#include + +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 diff --git a/external/android-emugl/host/libs/mir_support/shared_state.h b/src/anbox/graphics/mir_display_connection.h similarity index 64% rename from external/android-emugl/host/libs/mir_support/shared_state.h rename to src/anbox/graphics/mir_display_connection.h index f57771e..b0c9f29 100644 --- a/external/android-emugl/host/libs/mir_support/shared_state.h +++ b/src/anbox/graphics/mir_display_connection.h @@ -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 -#include +namespace anbox { +namespace graphics { -namespace mir { -namespace support { -class SharedState { +class MirDisplayConnection { public: - static std::shared_ptr 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 diff --git a/src/anbox/graphics/mir_native_window_creator.cpp b/src/anbox/graphics/mir_native_window_creator.cpp new file mode 100644 index 0000000..387f4da --- /dev/null +++ b/src/anbox/graphics/mir_native_window_creator.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/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 + +namespace anbox { +namespace graphics { +MirNativeWindowCreator::MirNativeWindowCreator(const std::shared_ptr &input_channel) : + input_channel_(input_channel), + display_connection_(std::make_shared()) { + + 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(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 diff --git a/src/anbox/graphics/mir_native_window_creator.h b/src/anbox/graphics/mir_native_window_creator.h new file mode 100644 index 0000000..fe8930c --- /dev/null +++ b/src/anbox/graphics/mir_native_window_creator.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_GRAPHICS_MIR_NATIVE_WINDOW_CREATOR_H_ +#define ANBOX_GRAPHICS_MIR_NATIVE_WINDOW_CREATOR_H_ + +#include "external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h" + +#include +#include + +namespace anbox { +class InputChannel; + +namespace graphics { + +class MirDisplayConnection; +class MirWindow; + +class MirNativeWindowCreator : public SubWindowHandler { +public: + MirNativeWindowCreator(const std::shared_ptr &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 input_channel_; + std::shared_ptr display_connection_; + std::map> windows_; +}; + +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/graphics/mir_window.cpp b/src/anbox/graphics/mir_window.cpp new file mode 100644 index 0000000..7ed3719 --- /dev/null +++ b/src/anbox/graphics/mir_window.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/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 &display, const std::shared_ptr &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_, ¶meters); + 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( + 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(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 diff --git a/src/anbox/graphics/mir_window.h b/src/anbox/graphics/mir_window.h new file mode 100644 index 0000000..45665f6 --- /dev/null +++ b/src/anbox/graphics/mir_window.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_GRAPHICS_MIR_WINDOW_H_ +#define ANBOX_GRAPHICS_MIR_WINDOW_H_ + +#define MIR_EGL_PLATFORM + +#include + +#include + +#include + +namespace anbox { +class InputChannel; + +namespace graphics { +class MirDisplayConnection; + +class MirWindow { +public: + MirWindow(const std::shared_ptr &display, const std::shared_ptr &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 input_channel_; + EGLNativeWindowType native_window_; + MirSurface *surface_; +}; + +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/id_map_writer.cpp b/src/anbox/id_map_writer.cpp new file mode 100644 index 0000000..b0a495e --- /dev/null +++ b/src/anbox/id_map_writer.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/id_map_writer.h" +#include "anbox/not_reachable.h" +#include "anbox/utils.h" + +#include +#include + +#include +#include + +#include + +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 diff --git a/src/anbox/id_map_writer.h b/src/anbox/id_map_writer.h new file mode 100644 index 0000000..cdd8a92 --- /dev/null +++ b/src/anbox/id_map_writer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_ID_MAP_WRITER_H_ +#define ANBOX_ID_MAP_WRITER_H_ + +#include +#include + +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 diff --git a/src/anbox/input_channel.cpp b/src/anbox/input_channel.cpp new file mode 100644 index 0000000..f8793c6 --- /dev/null +++ b/src/anbox/input_channel.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/input_channel.h" +#include "anbox/logger.h" + +#include + +#include + +#include +#include + +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 diff --git a/src/anbox/input_channel.h b/src/anbox/input_channel.h new file mode 100644 index 0000000..215eb2b --- /dev/null +++ b/src/anbox/input_channel.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_INPUT_CHANNEL_H_ +#define ANBOX_INPUT_CHANNEL_H_ + +#include + +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 diff --git a/src/anbox/namespace_attacher.cpp b/src/anbox/namespace_attacher.cpp new file mode 100644 index 0000000..b5a2706 --- /dev/null +++ b/src/anbox/namespace_attacher.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/namespace_attacher.h" +#include "anbox/utils.h" + +#include +#include + +#include +#include + +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 &types, pid_t pid) : + pid_(pid) { + + attach(types); +} + +NamespaceAttacher::~NamespaceAttacher() { +} + +void NamespaceAttacher::attach(const std::vector &types) { + std::vector 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 diff --git a/src/anbox/namespace_attacher.h b/src/anbox/namespace_attacher.h new file mode 100644 index 0000000..6e1ab13 --- /dev/null +++ b/src/anbox/namespace_attacher.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NAMESPACE_ATTACHER_H_ +#define ANBOX_NAMESPACE_ATTACHER_H_ + +#include +#include +#include + +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 &types, pid_t pid); + ~NamespaceAttacher(); + +private: + void attach(const std::vector &types); + + pid_t pid_; +}; +} // namespace + +#endif diff --git a/src/anbox/network/qemu_pipe_connection_creator.cpp b/src/anbox/network/qemu_pipe_connection_creator.cpp index f4c5e49..b548c51 100644 --- a/src/anbox/network/qemu_pipe_connection_creator.cpp +++ b/src/anbox/network/qemu_pipe_connection_creator.cpp @@ -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 QemuPipeConnectionCreator::create_processor(co return std::make_shared(renderer_socket_path_, runtime_, messenger); else if (type == client_type::qemud_boot_properties) return std::make_shared(messenger); + else if (type == client_type::qemud_hw_control) + return std::make_shared(messenger); + else if (type == client_type::qemud_sensors) + return std::make_shared(messenger); + else if (type == client_type::qemud_camera) + return std::make_shared(messenger); + else if (type == client_type::qemud_fingerprint) + return std::make_shared(messenger); return std::make_shared(); } diff --git a/src/anbox/network/qemu_pipe_connection_creator.h b/src/anbox/network/qemu_pipe_connection_creator.h index 122ae28..a132982 100644 --- a/src/anbox/network/qemu_pipe_connection_creator.h +++ b/src/anbox/network/qemu_pipe_connection_creator.h @@ -46,6 +46,10 @@ public: invalid, opengles, qemud_boot_properties, + qemud_hw_control, + qemud_sensors, + qemud_camera, + qemud_fingerprint, }; private: diff --git a/src/anbox/network/socket_messenger.cpp b/src/anbox/network/socket_messenger.cpp index e8885df..22199ed 100644 --- a/src/anbox/network/socket_messenger.cpp +++ b/src/anbox/network/socket_messenger.cpp @@ -51,14 +51,22 @@ void SocketMessenger::send(char const* data, size_t length) VariableLengthArray whole_message{length}; std::copy(data, data + length, whole_message.data()); - try { - std::unique_lock 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 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; } } diff --git a/src/anbox/not_reachable.cpp b/src/anbox/not_reachable.cpp new file mode 100644 index 0000000..6cc1cd2 --- /dev/null +++ b/src/anbox/not_reachable.cpp @@ -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 . + * + * Authored by: Thomas Voß + * + */ + +#include "anbox/not_reachable.h" + +#include + +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}; +} diff --git a/src/anbox/not_reachable.h b/src/anbox/not_reachable.h new file mode 100644 index 0000000..c234d0c --- /dev/null +++ b/src/anbox/not_reachable.h @@ -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 . + * + * Authored by: Thomas Voß + * + */ + +#ifndef ANBOX_UTIL_NOT_REACHABLE_H_ +#define ANBOX_UTIL_NOT_REACHABLE_H_ + +#include +#include + +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 diff --git a/src/anbox/runtime.cpp b/src/anbox/runtime.cpp index fac5976..41dac2d 100644 --- a/src/anbox/runtime.cpp +++ b/src/anbox/runtime.cpp @@ -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"; diff --git a/src/anbox/support/boot_properties_message_processor.cpp b/src/anbox/support/boot_properties_message_processor.cpp index e4120dd..d77c8e0 100644 --- a/src/anbox/support/boot_properties_message_processor.cpp +++ b/src/anbox/support/boot_properties_message_processor.cpp @@ -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 - -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 &messenger) : - messenger_(messenger) { + QemudMessageProcessor(messenger) { } BootPropertiesMessageProcessor::~BootPropertiesMessageProcessor() { } -bool BootPropertiesMessageProcessor::process_data(const std::vector &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(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(""), 1); + finish_message(); } } // namespace support } // namespace anbox diff --git a/src/anbox/support/boot_properties_message_processor.h b/src/anbox/support/boot_properties_message_processor.h index e5d2219..c281fd5 100644 --- a/src/anbox/support/boot_properties_message_processor.h +++ b/src/anbox/support/boot_properties_message_processor.h @@ -18,30 +18,20 @@ #ifndef ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_ #define ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_ -#include - -#include - -#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 &messenger); ~BootPropertiesMessageProcessor(); - bool process_data(const std::vector &data) override; +protected: + void handle_command(const std::string &command) override; private: - void process_commands(); void list_properties(); - - std::shared_ptr messenger_; - std::vector buffer_; }; } // namespace graphics } // namespace anbox diff --git a/src/anbox/support/camera_message_processor.cpp b/src/anbox/support/camera_message_processor.cpp new file mode 100644 index 0000000..8653ba1 --- /dev/null +++ b/src/anbox/support/camera_message_processor.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/camera_message_processor.h" + +namespace anbox { +namespace support { +CameraMessageProcessor::CameraMessageProcessor(const std::shared_ptr &messenger) : + messenger_(messenger) { +} + +CameraMessageProcessor::~CameraMessageProcessor() { +} + +bool CameraMessageProcessor::process_data(const std::vector &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(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 diff --git a/src/anbox/support/camera_message_processor.h b/src/anbox/support/camera_message_processor.h new file mode 100644 index 0000000..593b113 --- /dev/null +++ b/src/anbox/support/camera_message_processor.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#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 &messenger); + ~CameraMessageProcessor(); + + bool process_data(const std::vector &data) override; + +private: + void process_commands(); + + void handle_command(const std::string &command); + void list(); + + std::shared_ptr messenger_; + std::vector buffer_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/fingerprint_message_processor.cpp b/src/anbox/support/fingerprint_message_processor.cpp new file mode 100644 index 0000000..a68dc50 --- /dev/null +++ b/src/anbox/support/fingerprint_message_processor.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/fingerprint_message_processor.h" + +namespace anbox { +namespace support { +FingerprintMessageProcessor::FingerprintMessageProcessor(const std::shared_ptr &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 diff --git a/src/anbox/support/fingerprint_message_processor.h b/src/anbox/support/fingerprint_message_processor.h new file mode 100644 index 0000000..7fcda83 --- /dev/null +++ b/src/anbox/support/fingerprint_message_processor.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_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 &messenger); + ~FingerprintMessageProcessor(); + +protected: + void handle_command(const std::string &command) override; + +private: + void listen(); +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/hwcontrol_message_processor.cpp b/src/anbox/support/hwcontrol_message_processor.cpp index 8fcdd07..4e92af4 100644 --- a/src/anbox/support/hwcontrol_message_processor.cpp +++ b/src/anbox/support/hwcontrol_message_processor.cpp @@ -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 - -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 &messenger) : - messenger_(messenger) { + QemudMessageProcessor(messenger) { } HwControlMessageProcessor::~HwControlMessageProcessor() { } -bool HwControlMessageProcessor::process_data(const std::vector &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(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 diff --git a/src/anbox/support/hwcontrol_message_processor.h b/src/anbox/support/hwcontrol_message_processor.h index 45541be..aba9262 100644 --- a/src/anbox/support/hwcontrol_message_processor.h +++ b/src/anbox/support/hwcontrol_message_processor.h @@ -18,29 +18,17 @@ #ifndef ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_ #define ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_ -#include - -#include - -#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 &messenger); ~HwControlMessageProcessor(); - bool process_data(const std::vector &data) override; - -private: - void process_commands(); - - std::shared_ptr messenger_; - std::vector buffer_; +protected: + void handle_command(const std::string &command) override; }; } // namespace graphics } // namespace anbox diff --git a/src/anbox/support/null_message_processor.cpp b/src/anbox/support/null_message_processor.cpp index 5efa21d..581acbc 100644 --- a/src/anbox/support/null_message_processor.cpp +++ b/src/anbox/support/null_message_processor.cpp @@ -16,6 +16,8 @@ */ #include "anbox/support/null_message_processor.h" +#include "anbox/utils.h" +#include "anbox/logger.h" #include @@ -27,7 +29,8 @@ NullMessageProcessor::NullMessageProcessor() { NullMessageProcessor::~NullMessageProcessor() { } -bool NullMessageProcessor::process_data(const std::vector&) { +bool NullMessageProcessor::process_data(const std::vector &data) { + DEBUG("Received: %s", utils::hex_dump(data.data(), data.size())); return true; } } // namespace support diff --git a/src/anbox/support/null_message_processor.h b/src/anbox/support/null_message_processor.h index f46bb62..8b399a2 100644 --- a/src/anbox/support/null_message_processor.h +++ b/src/anbox/support/null_message_processor.h @@ -18,11 +18,6 @@ #ifndef ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_ #define ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_ -#include - -#include - -#include "anbox/runtime.h" #include "anbox/network/message_processor.h" namespace anbox { diff --git a/src/anbox/support/qemud_message_processor.cpp b/src/anbox/support/qemud_message_processor.cpp new file mode 100644 index 0000000..73663ea --- /dev/null +++ b/src/anbox/support/qemud_message_processor.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/support/qemud_message_processor.h" +#include "anbox/utils.h" +#include "anbox/logger.h" + +#include + +namespace { +static constexpr const long header_size{4}; +} + +namespace anbox { +namespace support { +QemudMessageProcessor::QemudMessageProcessor(const std::shared_ptr &messenger) : + messenger_(messenger) { +} + +QemudMessageProcessor::~QemudMessageProcessor() { +} + +bool QemudMessageProcessor::process_data(const std::vector &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(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(""), 1); +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/qemud_message_processor.h b/src/anbox/support/qemud_message_processor.h new file mode 100644 index 0000000..e57e853 --- /dev/null +++ b/src/anbox/support/qemud_message_processor.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#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 &messenger); + ~QemudMessageProcessor(); + + bool process_data(const std::vector &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 messenger_; + +private: + void process_commands(); + + std::vector buffer_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/sensors_message_processor.cpp b/src/anbox/support/sensors_message_processor.cpp new file mode 100644 index 0000000..ff89783 --- /dev/null +++ b/src/anbox/support/sensors_message_processor.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/sensors_message_processor.h" + +namespace anbox { +namespace support { +SensorsMessageProcessor::SensorsMessageProcessor(const std::shared_ptr &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 diff --git a/src/anbox/support/sensors_message_processor.h b/src/anbox/support/sensors_message_processor.h new file mode 100644 index 0000000..fd94646 --- /dev/null +++ b/src/anbox/support/sensors_message_processor.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_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 &messenger); + ~SensorsMessageProcessor(); + +protected: + void handle_command(const std::string &command) override; + +private: + void list_sensors(); +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp index 74f8882..3a9c1d4 100644 --- a/src/anbox/utils.cpp +++ b/src/anbox/utils.cpp @@ -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 diff --git a/src/anbox/utils.h b/src/anbox/utils.h index d614fa3..6d6a928 100644 --- a/src/anbox/utils.h +++ b/src/anbox/utils.h @@ -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 static std::string string_format(const std::string& fmt_str, Types&&... args); } // namespace utils diff --git a/src/container_main.cpp b/src/container_main.cpp new file mode 100644 index 0000000..d71300f --- /dev/null +++ b/src/container_main.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +extern "C" int bwrap_main(int argc, char **argv); + +int main(int argc, char **argv) { + return bwrap_main(argc, argv); +}