diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22113c4..c43be36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -192,6 +192,9 @@ set(SOURCES anbox/cmds/session_manager.cpp anbox/cmds/container_manager.cpp anbox/cmds/launch.cpp + anbox/cmds/system_info.cpp + + anbox/utils/environment_file.cpp anbox/do_not_copy_or_move.h anbox/optional.h diff --git a/src/anbox/cmds/system_info.cpp b/src/anbox/cmds/system_info.cpp new file mode 100644 index 0000000..372bc0a --- /dev/null +++ b/src/anbox/cmds/system_info.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/cmds/system_info.h" +#include "anbox/graphics/emugl/RenderApi.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/utils/environment_file.h" +#include "anbox/version.h" +#include "anbox/logger.h" +#include "anbox/version.h" + +#include +#include + +#include + +#include "OpenGLESDispatch/EGLDispatch.h" + +namespace fs = boost::filesystem; + +namespace { +constexpr const char *os_release_path{"/etc/os-release"}; +constexpr const char *proc_version_path{"/proc/version"}; +constexpr const char *binder_path{"/dev/binder"}; +constexpr const char *ashmem_path{"/dev/ashmem"}; + +class SystemInformation { + public: + SystemInformation() { + collect_os_info(); + collect_kernel_info(); + collect_graphics_info(); + } + + std::string to_text() const { + std::stringstream s; + + s << "version: " + << anbox::utils::string_format("%d.%d.%d", + anbox::build::version_major, + anbox::build::version_minor, + anbox::build::version_patch) + << std::endl; + + s << "os:" << std::endl + << " name: " << os_info_.name << std::endl + << " version: " << os_info_.version << std::endl + << " snap-based: " << std::boolalpha << os_info_.snap_based << std::endl; + + s << "kernel:" << std::endl + << " version: " << kernel_info_.version << std::endl + << " binder: " << std::boolalpha << kernel_info_.binder << std::endl + << " ashmem: " << std::boolalpha << kernel_info_.ashmem << std::endl; + + auto print_extensions = [](const std::vector &extensions) { + std::stringstream s; + if (extensions.size() > 0) { + s << std::endl; + for (const auto &ext : extensions) { + if (ext.length() == 0) + continue; + s << " - " << ext << std::endl; + } + } else { + s << " []" << std::endl; + } + return s.str(); + }; + + s << "graphics:" << std::endl + << " egl:" << std::endl + << " vendor: " << graphics_info_.egl_vendor << std::endl + << " version: " << graphics_info_.egl_version << std::endl + << " extensions:" << print_extensions(graphics_info_.egl_extensions) + << " gles2:" << std::endl + << " vendor: " << graphics_info_.gles2_vendor << std::endl + << " vendor: " << graphics_info_.gles2_version << std::endl + << " extensions:" << print_extensions(graphics_info_.gles2_extensions); + + return s.str(); + } + + private: + void collect_os_info() { + os_info_.snap_based = (getenv("SNAP") != nullptr); + if (fs::exists(os_release_path)) { + anbox::utils::EnvironmentFile os_release(os_release_path); + os_info_.name = os_release.value("NAME"); + os_info_.version = os_release.value("VERSION"); + } else if (os_info_.snap_based) { + // As we can't read /etc/os-release and we're snap-based this is the best we can guess + os_info_.name = "Ubuntu"; + os_info_.version = "16"; + } + } + + void collect_kernel_info() { + if (fs::exists(proc_version_path)) { + std::ifstream in(proc_version_path); + std::getline(in, kernel_info_.version); + } + + kernel_info_.binder = fs::exists(binder_path); + kernel_info_.ashmem = fs::exists(ashmem_path); + } + + void collect_graphics_info() { + auto gl_libs = anbox::graphics::emugl::default_gl_libraries(true); + if (!anbox::graphics::emugl::initialize(gl_libs, nullptr, nullptr)) { + return; + } + + auto display = s_egl.eglGetDisplay(0); + if (display != EGL_NO_DISPLAY) { + s_egl.eglInitialize(display, nullptr, nullptr); + graphics_info_.egl_vendor = s_egl.eglQueryString(display, EGL_VENDOR); + graphics_info_.egl_version = s_egl.eglQueryString(display, EGL_VERSION); + graphics_info_.egl_extensions = anbox::utils::string_split(s_egl.eglQueryString(display, EGL_EXTENSIONS), ' '); + + GLint config_attribs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE}; + + EGLConfig config; + int n; + if (s_egl.eglChooseConfig(display, config_attribs, &config, 1, &n) && n > 0) { + GLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE}; + auto context = s_egl.eglCreateContext(display, config, nullptr, attribs); + if (context != EGL_NO_CONTEXT) { + // We require surfaceless-context support here for now. If eglMakeCurrent fails + // glGetString will return null below which we handle correctly. + s_egl.eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); + + auto safe_get_string = [](GLint item) { + auto str = s_gles2.glGetString(item); + if (!str) + return std::string("n/a"); + return std::string(reinterpret_cast(str)); + }; + + graphics_info_.gles2_vendor = safe_get_string(GL_VENDOR); + graphics_info_.gles2_version = safe_get_string(GL_VERSION); + graphics_info_.gles2_extensions = anbox::utils::string_split(safe_get_string(GL_EXTENSIONS), ' '); + + s_egl.eglMakeCurrent(display, nullptr, nullptr, nullptr); + s_egl.eglDestroyContext(display, context); + } + } + } + } + + struct { + bool snap_based = false; + std::string name = "n/a"; + std::string version = "n/a"; + } os_info_; + + struct { + std::string version = "n/a"; + bool binder = false; + bool ashmem = false; + } kernel_info_; + + struct { + std::string egl_vendor = "n/a"; + std::string egl_version = "n/a"; + std::vector egl_extensions; + std::string gles2_vendor = "n/a"; + std::string gles2_version = "n/a"; + std::vector gles2_extensions; + } graphics_info_; +}; + +std::ostream &operator<<(std::ostream &out, const SystemInformation &info) { + out << info.to_text(); + return out; +} +} + +anbox::cmds::SystemInfo::SystemInfo() + : CommandWithFlagsAndAction{ + cli::Name{"system-info"}, cli::Usage{"system-info"}, + cli::Description{"Print various information about the system we're running on"}} { + action([](const cli::Command::Context& ctxt) { + SystemInformation si; + std::cout << si; + return EXIT_SUCCESS; + }); +} diff --git a/src/anbox/cmds/system_info.h b/src/anbox/cmds/system_info.h new file mode 100644 index 0000000..795f652 --- /dev/null +++ b/src/anbox/cmds/system_info.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_CMDS_SYSTEM_INFO_H_ +#define ANBOX_CMDS_SYSTEM_INFO_H_ + +#include +#include +#include + +#include "anbox/cli.h" + +namespace anbox { +namespace cmds { +class SystemInfo : public cli::CommandWithFlagsAndAction { + public: + SystemInfo(); +}; +} // namespace cmds +} // namespace anbox + +#endif diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp index 6bb4e84..a40da89 100644 --- a/src/anbox/daemon.cpp +++ b/src/anbox/daemon.cpp @@ -24,6 +24,7 @@ #include "anbox/cmds/container_manager.h" #include "anbox/cmds/session_manager.h" +#include "anbox/cmds/system_info.h" #include "anbox/cmds/launch.h" #include "anbox/cmds/version.h" @@ -38,12 +39,14 @@ Daemon::Daemon() cmd.command(std::make_shared()) .command(std::make_shared()) .command(std::make_shared()) - .command(std::make_shared()); + .command(std::make_shared()) + .command(std::make_shared()); + Log().Init(anbox::Logger::Severity::kWarning); const auto log_level = utils::get_env_value("ANBOX_LOG_LEVEL", ""); - if (!log_level.empty() || !Log().SetSeverityFromString(log_level)) + if (!log_level.empty() && !Log().SetSeverityFromString(log_level)) WARNING("Failed to set logging severity to '%s'", log_level); } diff --git a/src/anbox/graphics/emugl/RenderApi.cpp b/src/anbox/graphics/emugl/RenderApi.cpp index 934be25..dc07604 100644 --- a/src/anbox/graphics/emugl/RenderApi.cpp +++ b/src/anbox/graphics/emugl/RenderApi.cpp @@ -49,10 +49,12 @@ std::vector default_gl_libraries(bool no_glesv1) { }; } -bool initialize(const std::vector &libs, emugl_logger_struct log_funcs, logger_t crash_func) { +bool initialize(const std::vector &libs, emugl_logger_struct *log_funcs, logger_t crash_func) { set_emugl_crash_reporter(crash_func); - set_emugl_logger(log_funcs.coarse); - set_emugl_cxt_logger(log_funcs.fine); + if (log_funcs) { + set_emugl_logger(log_funcs->coarse); + set_emugl_cxt_logger(log_funcs->fine); + } for (const auto &lib : libs) { const auto path = lib.path.c_str(); diff --git a/src/anbox/graphics/emugl/RenderApi.h b/src/anbox/graphics/emugl/RenderApi.h index 61c2c28..65cab84 100644 --- a/src/anbox/graphics/emugl/RenderApi.h +++ b/src/anbox/graphics/emugl/RenderApi.h @@ -39,7 +39,7 @@ struct GLLibrary { std::vector default_gl_libraries(bool no_glesv1 = false); -bool initialize(const std::vector &libs, emugl_logger_struct log_funcs, logger_t crash_func); +bool initialize(const std::vector &libs, emugl_logger_struct *log_funcs, logger_t crash_func); } // namespace emugl } // namespace graphics } // namespace anbox diff --git a/src/anbox/graphics/gl_renderer_server.cpp b/src/anbox/graphics/gl_renderer_server.cpp index 4da77f5..b28ee10 100644 --- a/src/anbox/graphics/gl_renderer_server.cpp +++ b/src/anbox/graphics/gl_renderer_server.cpp @@ -68,7 +68,7 @@ GLRendererServer::GLRendererServer(const Config &config, const std::shared_ptr(renderer_, wm)) { - std::vector gl_libs = emugl::default_gl_libraries(true); + auto gl_libs = emugl::default_gl_libraries(true); if (config.driver == Config::Driver::Translator) { DEBUG("Using GLES-to-GL translator for rendering"); @@ -82,7 +82,7 @@ GLRendererServer::GLRendererServer(const Config &config, const std::shared_ptrinitialize(0); diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp index 83cefc2..a4f0c8f 100644 --- a/src/anbox/utils.cpp +++ b/src/anbox/utils.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include @@ -95,6 +97,18 @@ bool string_starts_with(const std::string &text, const std::string &prefix) { return text.compare(0, prefix.size(), prefix) == 0; } +std::vector string_split(const std::string &text, char sep) { + std::vector tokens; + return boost::algorithm::split(tokens, text, boost::is_from_range(sep, sep), boost::algorithm::token_compress_on); +} + +std::string strip_surrounding_quotes(const std::string &text) { + auto result = text; + if (text[0] == '\"' && text[text.length() - 1] == '\"') + result = text.substr(1, text.length() - 2); + return result; +} + std::string hex_dump(const uint8_t *data, uint32_t size) { unsigned char buff[17]; const uint8_t *pc = data; diff --git a/src/anbox/utils.h b/src/anbox/utils.h index d06d9da..28b95a1 100644 --- a/src/anbox/utils.h +++ b/src/anbox/utils.h @@ -37,6 +37,10 @@ int write_file_at(int dirfd, const char *path, const char *content); bool string_starts_with(const std::string &text, const std::string &prefix); +std::vector string_split(const std::string &text, char sep); + +std::string strip_surrounding_quotes(const std::string &text); + std::string hex_dump(const uint8_t *data, uint32_t size); std::string get_env_value(const std::string &name, diff --git a/src/anbox/utils/environment_file.cpp b/src/anbox/utils/environment_file.cpp new file mode 100644 index 0000000..5285e0e --- /dev/null +++ b/src/anbox/utils/environment_file.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/utils/environment_file.h" +#include "anbox/utils.h" + +#include + +namespace anbox { +namespace utils { +EnvironmentFile::EnvironmentFile(const boost::filesystem::path &path) { + std::ifstream in(path.string()); + std::string line; + while (std::getline(in, line)) { + auto tokens = utils::string_split(line, '='); + if (tokens.size() != 2) + continue; + data_[tokens[0]] = utils::strip_surrounding_quotes(tokens[1]); + } +} + +std::string EnvironmentFile::value(const std::string &key, const std::string &default_value) const { + auto iter = data_.find(key); + if (iter == data_.end()) + return default_value; + return iter->second; +} +} // namespace utils +} // namespace anbox diff --git a/src/anbox/utils/environment_file.h b/src/anbox/utils/environment_file.h new file mode 100644 index 0000000..14e00e9 --- /dev/null +++ b/src/anbox/utils/environment_file.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_UTILS_ENVIRONMENT_FILE_H_ +#define ANBOX_UTILS_ENVIRONMENT_FILE_H_ + +#include +#include +#include + +namespace anbox { +namespace utils { +class EnvironmentFile { + public: + EnvironmentFile(const boost::filesystem::path &path); + ~EnvironmentFile() = default; + + std::string value(const std::string &key, const std::string &default_value = "") const; + + private: + std::map data_; +}; +} // namespace utils +} // namespace anbox + +#endif