diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f5d939..e4fc84b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.11.4) # for RHEL 8 project( linyaps-box - VERSION 2.1.0 + VERSION 2.1.3 DESCRIPTION "A simple OCI runtime for desktop applications" HOMEPAGE_URL "https://github.com/OpenAtom-Linyaps/linyaps-box" LANGUAGES CXX) @@ -91,7 +91,7 @@ set(linyaps-box_ACTIVE_LOG_LEVEL ) set(linyaps-box_ENABLE_CPM - ON + OFF CACHE BOOL "enable CPM") if(CMAKE_VERSION VERSION_LESS "3.14") @@ -105,11 +105,6 @@ set(linyaps-box_CPM_LOCAL_PACKAGES_ONLY OFF CACHE BOOL "use local packages only") -if(linyaps-box_CPM_LOCAL_PACKAGES_ONLY) - message(STATUS "CPM is disabled") - set(linyaps-box_ENABLE_CPM OFF) -endif() - # ============================================================================== set(linyaps-box_LIBRARY linyaps-box) @@ -118,7 +113,7 @@ set(linyaps-box_LIBRARY_SOURCE src/linyaps_box/app.cpp src/linyaps_box/app.h src/linyaps_box/cgroup.h - src/linyaps_box/cgroup_manager.c + src/linyaps_box/cgroup_manager.cpp src/linyaps_box/cgroup_manager.h src/linyaps_box/command/exec.cpp src/linyaps_box/command/exec.h @@ -195,8 +190,7 @@ if(NOT linyaps-box_MAKE_RELEASE) COMMAND git rev-parse --short=7 HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_COMMIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) set(LINYAPS_BOX_VERSION "${LINYAPS_BOX_VERSION}-dev-${GIT_COMMIT_HASH}") endif() @@ -229,6 +223,10 @@ if(linyaps-box_ENABLE_CPM) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(CPM) + if (linyaps-box_CPM_LOCAL_PACKAGES_ONLY) + set(CPM_USE_LOCAL_PACKAGES ON) + endif() + CPMFindPackage( NAME nlohmann_json VERSION 3.11.3 @@ -238,7 +236,7 @@ if(linyaps-box_ENABLE_CPM) OPTIONS "JSON_BuildTests OFF") CPMFindPackage( NAME CLI11 - VERSION 2.4.1 + VERSION 2.5.0 GITHUB_REPOSITORY CLIUtils/CLI11 GIT_TAG v2.5.0 EXCLUDE_FROM_ALL ON @@ -257,12 +255,12 @@ endif() list(APPEND linyaps-box_LIBRARY_LINK_LIBRARIES PUBLIC nlohmann_json::nlohmann_json) -find_package(CLI11 2.4.1 QUIET) +find_package(CLI11 2.5.0 QUIET) if(NOT CLI11_FOUND) add_subdirectory(external/CLI11) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake.external/CLI11") - find_package(CLI11 2.4.1 REQUIRED) + find_package(CLI11 2.5.0 REQUIRED) message(STATUS "use vendor CLI11 ${CLI11_VERSION}") endif() @@ -438,7 +436,8 @@ function(setup_linyaps_box_smoke_tests) ./tests/ll-box-st/09-check-rlimit.json ./tests/ll-box-st/10-check-oom.json ./tests/ll-box-st/11-output-to-null.json - ./tests/ll-box-st/12-bind-host-dev.json) + ./tests/ll-box-st/12-bind-host-dev.json + ./tests/ll-box-st/13-pid-extension.json) foreach(test ${linyaps-box_SMOKE_TESTS}) add_test( diff --git a/CMakePresets.json b/CMakePresets.json index d6d16d6..5265a89 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 10, "cmakeMinimumRequired": { "major": 3, "minor": 25, @@ -21,7 +21,8 @@ "linyaps-box_CPACK_PACKAGING_INSTALL_PREFIX": "", "linyaps-box_ENABLE_SMOKE_TESTS": true, "linyaps-box_DEFAULT_LOG_LEVEL": "7", - "linyaps-box_ACTIVE_LOG_LEVEL": "7" + "linyaps-box_ACTIVE_LOG_LEVEL": "7", + "linyaps-box_ENABLE_CPM": "ON" } }, { @@ -47,7 +48,9 @@ "CMAKE_COLOR_DIAGNOSTICS": true, "CMAKE_CXX_FLAGS": "-fno-asynchronous-unwind-tables -fdata-sections -ffunction-sections -flto=auto $env{CXXFLAGS}", "CMAKE_EXE_LINKER_FLAGS_INIT": "-Wl,--gc-sections,--strip-all,--exclude-libs,ALL -flto=auto", - "linyaps-box_STATIC": true + "linyaps-box_STATIC": true, + "linyaps-box_ENABLE_CPACK": "ON", + "linyaps-box_MAKE_RELEASE": "ON" } }, { diff --git a/README.md b/README.md index 3c2c888..f6fb96b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ \[ **en** | [zh_CN](./README.zh_CN.md) \] +[![Packaging status](https://repology.org/badge/vertical-allrepos/linyaps-box.svg)](https://repology.org/project/linyaps-box/versions) + This project is a simple [OCI runtime] mainly used by [linyaps], which is a toolkit for Linux desktop application distributing. diff --git a/README.zh_CN.md b/README.zh_CN.md index 6378383..8bcbc23 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -2,6 +2,8 @@ \[ [en](./README.md) | **zh_CN** \] +[![Packaging status](https://repology.org/badge/vertical-allrepos/linyaps-box.svg)](https://repology.org/project/linyaps-box/versions) + 这个项目是一个简单的[OCI运行时],主要由[玲珑]使用, 玲珑是一个用于Linux桌面应用程序分发的工具包。 diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake index eea2921..3636ee5 100644 --- a/cmake/CPM.cmake +++ b/cmake/CPM.cmake @@ -45,7 +45,7 @@ endif() if(DEFINED EXTRACTED_CPM_VERSION) set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}") else() - set(CURRENT_CPM_VERSION 0.40.8) + set(CURRENT_CPM_VERSION 0.42.0) endif() get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) @@ -202,6 +202,60 @@ function(cpm_package_name_from_git_uri URI RESULT) endif() endfunction() +# Find the shortest hash that can be used eg, if origin_hash is +# cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found +# that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash} +# +# * .../cccb.hash +# * .../cccb77ae.hash +# * .../cccb77ae9609.hash +# * .../cccb77ae9609d276.hash +# * etc +# +# We will be able to use a shorter path with very high probability, but in the (rare) event that the +# first couple characters collide, we will check longer and longer substrings. +function(cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var) + # for compatibility with caches populated by a previous version of CPM, check if a directory using + # the full hash already exists + if(EXISTS "${source_cache_dir}/${origin_hash}") + set(${short_hash_output_var} + "${origin_hash}" + PARENT_SCOPE + ) + return() + endif() + + foreach(len RANGE 4 40 4) + string(SUBSTRING "${origin_hash}" 0 ${len} short_hash) + set(hash_lock ${source_cache_dir}/${short_hash}.lock) + set(hash_fp ${source_cache_dir}/${short_hash}.hash) + # Take a lock, so we don't have a race condition with another instance of cmake. We will release + # this lock when we can, however, if there is an error, we want to ensure it gets released on + # it's own on exit from the function. + file(LOCK ${hash_lock} GUARD FUNCTION) + + # Load the contents of .../${short_hash}.hash + file(TOUCH ${hash_fp}) + file(READ ${hash_fp} hash_fp_contents) + + if(hash_fp_contents STREQUAL "") + # Write the origin hash + file(WRITE ${hash_fp} ${origin_hash}) + file(LOCK ${hash_lock} RELEASE) + break() + elseif(hash_fp_contents STREQUAL origin_hash) + file(LOCK ${hash_lock} RELEASE) + break() + else() + file(LOCK ${hash_lock} RELEASE) + endif() + endforeach() + set(${short_hash_output_var} + "${short_hash}" + PARENT_SCOPE + ) +endfunction() + # Try to infer package name and version from a url function(cpm_package_name_and_ver_from_url url outName outVer) if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") @@ -594,14 +648,6 @@ endfunction() function(CPMAddPackage) cpm_set_policies() - list(LENGTH ARGN argnLength) - if(argnLength EQUAL 1) - cpm_parse_add_package_single_arg("${ARGN}" ARGN) - - # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM - set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") - endif() - set(oneValueArgs NAME FORCE @@ -624,10 +670,26 @@ function(CPMAddPackage) set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES) + list(LENGTH ARGN argnLength) + + # Parse single shorthand argument + if(argnLength EQUAL 1) + cpm_parse_add_package_single_arg("${ARGN}" ARGN) + + # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM + set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") + + # Parse URI shorthand argument + elseif(argnLength GREATER 1 AND "${ARGV0}" STREQUAL "URI") + list(REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag" + cpm_parse_add_package_single_arg("${ARGV1}" ARGV0) + + set(ARGN "${ARGV0};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN}") + endif() + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") # Set default values for arguments - if(NOT DEFINED CPM_ARGS_VERSION) if(DEFINED CPM_ARGS_GIT_TAG) cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) @@ -798,9 +860,19 @@ function(CPMAddPackage) set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY}) elseif(CPM_USE_NAMED_CACHE_DIRECTORIES) string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") + cpm_get_shortest_hash( + "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory + "${origin_hash}" # Input hash + origin_hash # Computed hash + ) set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) else() string(SHA1 origin_hash "${origin_parameters}") + cpm_get_shortest_hash( + "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory + "${origin_hash}" # Input hash + origin_hash # Computed hash + ) set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) endif() # Expand `download_directory` relative path. This is important because EXISTS doesn't work for diff --git a/src/linyaps_box/app.cpp b/src/linyaps_box/app.cpp index 9fa08f5..77a445e 100644 --- a/src/linyaps_box/app.cpp +++ b/src/linyaps_box/app.cpp @@ -9,8 +9,7 @@ #include "linyaps_box/command/list.h" #include "linyaps_box/command/run.h" #include "linyaps_box/utils/log.h" - -#include +#include "utils/log.h" namespace { @@ -32,19 +31,19 @@ namespace linyaps_box { // Command line arguments are parsed according to // https://github.com/opencontainers/runtime-tools/blob/v0.9.0/docs/command-line-interface.md // Extended commands and options should be compatible with crun. -int main(int argc, char **argv) noexcept +auto main(int argc, char **argv) noexcept -> int try { LINYAPS_BOX_DEBUG() << "linyaps box called with" << [=]() -> std::string { std::stringstream result; for (int i = 0; i < argc; ++i) { result << " \""; - for (const char *c = argv[i]; *c != '\0'; ++c) { - if (*c == '\\') { + for (const auto ch : std::string_view(argv[i])) { // NOLINT + if (ch == '\\') { result << "\\\\"; - } else if (*c == '"') { + } else if (ch == '"') { result << "\\\""; } else { - result << *c; + result << ch; } } result << "\""; @@ -52,18 +51,18 @@ try { return result.str(); }(); - command::options options = command::parse(argc, argv); - if (options.global.return_code != 0) { - return options.global.return_code; + auto opts = command::parse(argc, argv); + if (opts.global.return_code != 0) { + return opts.global.return_code; } return std::visit(subCommand{ [](const command::list_options &options) { command::list(options); return 0; }, - [](const command::exec_options &options) { + [](const command::exec_options &options) -> int { command::exec(options); - return 0; + __builtin_unreachable(); }, [](const command::kill_options &options) { command::kill(options); @@ -72,10 +71,10 @@ try { [](const command::run_options &options) { return command::run(options); }, - [code = options.global.return_code](const std::monostate &) { - return code; + [](const std::monostate &) { + return 0; } }, - options.subcommand_opt); + opts.subcommand_opt); } catch (const std::exception &e) { LINYAPS_BOX_ERR() << "Error: " << e.what(); return -1; diff --git a/src/linyaps_box/app.h b/src/linyaps_box/app.h index 455428d..a813c7a 100644 --- a/src/linyaps_box/app.h +++ b/src/linyaps_box/app.h @@ -6,6 +6,6 @@ namespace linyaps_box { -int main(int argc, char **argv) noexcept; +auto main(int argc, char **argv) noexcept -> int; } // namespace linyaps_box diff --git a/src/linyaps_box/cgroup.h b/src/linyaps_box/cgroup.h index d0783db..fdc7cad 100644 --- a/src/linyaps_box/cgroup.h +++ b/src/linyaps_box/cgroup.h @@ -10,47 +10,52 @@ namespace linyaps_box { -enum class cgroup_manager_t : std::uint16_t { disabled, systemd, cgroupfs }; +enum class cgroup_manager_t : std::uint8_t { disabled, systemd, cgroupfs }; struct cgroup_options { std::unordered_map annotations; std::filesystem::path cgroup_path; - std::string id; std::filesystem::path state_root; + std::string id; pid_t pid; // resources and so on... }; struct cgroup_status { - std::filesystem::path path; - std::string scope; +public: + [[nodiscard]] auto path() const noexcept -> std::filesystem::path { return path_; } - [[nodiscard]] cgroup_manager_t manager_type() const noexcept { return manager; } + [[nodiscard]] auto scope() const noexcept -> std::string_view { return scope_; } + + [[nodiscard]] auto manager() const noexcept -> cgroup_manager_t { return manager_; } private: friend class cgroup_manager; - cgroup_manager_t manager; + std::filesystem::path path_; + std::string scope_; + cgroup_manager_t manager_; }; -inline std::ostream &operator<<(std::ostream &os, cgroup_manager_t manager) +inline auto operator<<(std::ostream &stream, cgroup_manager_t manager) -> std::ostream & { switch (manager) { case cgroup_manager_t::disabled: { - os << "disabled"; + stream << "disabled"; } break; case cgroup_manager_t::systemd: { - os << "systemd"; + stream << "systemd"; } break; case cgroup_manager_t::cgroupfs: { - os << "cgroupfs"; + stream << "cgroupfs"; + } break; + default: { + stream << "unknown"; } break; - default: - os << "unknown"; } - return os; + return stream; } } // namespace linyaps_box diff --git a/src/linyaps_box/cgroup_manager.c b/src/linyaps_box/cgroup_manager.cpp similarity index 62% rename from src/linyaps_box/cgroup_manager.c rename to src/linyaps_box/cgroup_manager.cpp index 66432c5..2dec2f6 100644 --- a/src/linyaps_box/cgroup_manager.c +++ b/src/linyaps_box/cgroup_manager.cpp @@ -2,6 +2,10 @@ // // SPDX-License-Identifier: LGPL-3.0-or-later -#pragma once - #include + +namespace linyaps_box { + +cgroup_manager::~cgroup_manager() = default; + +} // namespace linyaps_box diff --git a/src/linyaps_box/cgroup_manager.h b/src/linyaps_box/cgroup_manager.h index f7dde88..8bf1b52 100644 --- a/src/linyaps_box/cgroup_manager.h +++ b/src/linyaps_box/cgroup_manager.h @@ -13,9 +13,17 @@ namespace linyaps_box { class cgroup_manager : public virtual interface { public: - [[nodiscard]] virtual cgroup_manager_t type() const = 0; + cgroup_manager() = default; + ~cgroup_manager() override; - virtual cgroup_status create_cgroup(const cgroup_options &options) = 0; + cgroup_manager(const cgroup_manager &) = delete; + auto operator=(const cgroup_manager &) -> cgroup_manager & = delete; + cgroup_manager(cgroup_manager &&) = delete; + auto operator=(cgroup_manager &&) -> cgroup_manager & = delete; + + [[nodiscard]] virtual auto type() const -> cgroup_manager_t = 0; + + virtual auto create_cgroup(const cgroup_options &options) -> cgroup_status = 0; virtual void precreate_cgroup(const cgroup_options &options, utils::file_descriptor &dirfd) = 0; @@ -24,9 +32,9 @@ class cgroup_manager : public virtual interface // TODO: support update resource // virtual void update_resource(const cgroup_status &status, ) = 0; protected: - static void set_manager_type(cgroup_status &status, cgroup_manager_t type) noexcept + static void set_manager(cgroup_status &status, cgroup_manager_t type) noexcept { - status.manager = type; + status.manager_ = type; } }; diff --git a/src/linyaps_box/command/exec.cpp b/src/linyaps_box/command/exec.cpp index 71683c0..0ffd442 100644 --- a/src/linyaps_box/command/exec.cpp +++ b/src/linyaps_box/command/exec.cpp @@ -11,7 +11,7 @@ void linyaps_box::command::exec(const struct exec_options &options) { std::unique_ptr dir = - std::make_unique(options.global.get().root); + std::make_unique(options.global_.get().root); runtime_t runtime(std::move(dir)); auto container_refs = runtime.containers(); diff --git a/src/linyaps_box/command/kill.cpp b/src/linyaps_box/command/kill.cpp index 09527db..d716bc7 100644 --- a/src/linyaps_box/command/kill.cpp +++ b/src/linyaps_box/command/kill.cpp @@ -28,7 +28,7 @@ void linyaps_box::command::kill(const struct kill_options &options) break; } - auto status_dir = std::make_unique(options.global.get().root); + auto status_dir = std::make_unique(options.global_.get().root); if (!status_dir) { throw std::runtime_error("failed to create status directory"); } diff --git a/src/linyaps_box/command/list.cpp b/src/linyaps_box/command/list.cpp index 65f5b0b..a5aeb79 100644 --- a/src/linyaps_box/command/list.cpp +++ b/src/linyaps_box/command/list.cpp @@ -13,7 +13,7 @@ void linyaps_box::command::list(const struct list_options &options) { - auto status_dir = std::make_unique(options.global.get().root); + auto status_dir = std::make_unique(options.global_.get().root); if (!status_dir) { throw std::runtime_error("failed to create status directory"); } diff --git a/src/linyaps_box/command/options.cpp b/src/linyaps_box/command/options.cpp index cb37d1b..c3a7bf5 100644 --- a/src/linyaps_box/command/options.cpp +++ b/src/linyaps_box/command/options.cpp @@ -61,6 +61,10 @@ linyaps_box::command::options linyaps_box::command::parse(int argc, char *argv[] cmd_run->add_option("-b,--bundle", run_opt.bundle, "Path to the OCI bundle")->default_val("."); cmd_run->add_option("-f,--config", run_opt.config, "Override the configuration file to use") ->default_val("config.json"); + cmd_run->add_option("--preserve-fds", + run_opt.preserve_fds, + "Pass N additional file descriptors to the container") + ->default_val(0); exec_options exec_opt{ options.global }; auto *cmd_exec = app.add_subcommand("exec", "Exec a command in a running container") @@ -111,6 +115,7 @@ linyaps_box::command::options linyaps_box::command::parse(int argc, char *argv[] app.parse(argc, argv); } catch (const CLI::ParseError &e) { options.global.return_code = app.exit(e); + return options; } if (cmd_list->parsed()) { diff --git a/src/linyaps_box/command/options.h b/src/linyaps_box/command/options.h index b5b6c5d..de6b739 100644 --- a/src/linyaps_box/command/options.h +++ b/src/linyaps_box/command/options.h @@ -22,30 +22,27 @@ struct global_options struct list_options { - // FIXME: if the underlying type of enum class is std::uint8_t, - // the mapping message of CLI11 transformer is incorrect - // use std::uint16_t for now - enum class output_format_t : std::uint16_t { table, json }; + enum class output_format_t : std::uint8_t { table, json }; explicit list_options(global_options &global) - : global(global) + : global_(global) { } output_format_t output_format{ output_format_t::table }; - std::reference_wrapper global; + std::reference_wrapper global_; }; struct exec_options { explicit exec_options(global_options &global) : no_new_privs(false) - , global(global) + , global_(global) { } bool no_new_privs; - std::reference_wrapper global; + std::reference_wrapper global_; std::vector command; std::string user; std::optional> caps; @@ -57,24 +54,25 @@ struct exec_options struct run_options { explicit run_options(global_options &global) - : global(global) + : global_(global) { } - std::reference_wrapper global; + std::reference_wrapper global_; std::string ID; std::string bundle; std::string config; + int preserve_fds; }; struct kill_options { explicit kill_options(global_options &global) - : global(global) + : global_(global) { } - std::reference_wrapper global; + std::reference_wrapper global_; std::string container; std::string signal; }; diff --git a/src/linyaps_box/command/run.cpp b/src/linyaps_box/command/run.cpp index 3c10804..e15fad3 100644 --- a/src/linyaps_box/command/run.cpp +++ b/src/linyaps_box/command/run.cpp @@ -8,18 +8,17 @@ #include "linyaps_box/runtime.h" #include "linyaps_box/status_directory.h" -int linyaps_box::command::run(const struct run_options &options) +auto linyaps_box::command::run(const struct run_options &options) -> int { std::unique_ptr dir = - std::make_unique(options.global.get().root); + std::make_unique(options.global_.get().root); runtime_t runtime(std::move(dir)); - runtime_t::create_container_options_t create_container_options; - create_container_options.bundle = options.bundle; - create_container_options.config = options.config; - create_container_options.ID = options.ID; - create_container_options.manager = options.global.get().manager; + const create_container_options_t create_container_options{ options.global_.get().manager, + options.preserve_fds, + options.ID, + options.bundle, + options.config }; auto container = runtime.create_container(create_container_options); - return container.run(container.get_config().process); } diff --git a/src/linyaps_box/command/run.h b/src/linyaps_box/command/run.h index e42c0a0..d5f23ea 100644 --- a/src/linyaps_box/command/run.h +++ b/src/linyaps_box/command/run.h @@ -8,6 +8,6 @@ namespace linyaps_box::command { -[[nodiscard]] int run(const run_options &options); +[[nodiscard]] auto run(const run_options &options) -> int; } // namespace linyaps_box::command diff --git a/src/linyaps_box/config.cpp b/src/linyaps_box/config.cpp index dbb0190..273e88b 100644 --- a/src/linyaps_box/config.cpp +++ b/src/linyaps_box/config.cpp @@ -11,17 +11,19 @@ namespace { -std::tuple -parse_mount_options(const std::vector &options) +// This function is used to parse the mount options from the config file and it only will be called +// once. +auto parse_mount_options(const std::vector &options) + -> std::tuple { - const static std::unordered_map propagation_flags_map{ + const std::unordered_map propagation_flags_map{ { "rprivate", MS_PRIVATE | MS_REC }, { "private", MS_PRIVATE }, { "rslave", MS_SLAVE | MS_REC }, { "slave", MS_SLAVE }, { "rshared", MS_SHARED | MS_REC }, { "shared", MS_SHARED }, { "runbindable", MS_UNBINDABLE | MS_REC }, { "unbindable", MS_UNBINDABLE }, }; - const static std::unordered_map flags_map{ + const std::unordered_map flags_map{ { "bind", MS_BIND }, { "defaults", 0 }, { "dirsync", MS_DIRSYNC }, @@ -43,7 +45,7 @@ parse_mount_options(const std::vector &options) { "sync", MS_SYNCHRONOUS }, }; - const static std::unordered_map unset_flags_map{ + const std::unordered_map unset_flags_map{ { "async", MS_SYNCHRONOUS }, { "atime", MS_NOATIME }, { "dev", MS_NODEV }, @@ -60,7 +62,7 @@ parse_mount_options(const std::vector &options) { "symfollow", LINGYAPS_MS_NOSYMFOLLOW }, }; - const static std::unordered_map extra_flags_map{ + const std::unordered_map extra_flags_map{ { "copy-symlink", linyaps_box::config::mount_t::COPY_SYMLINK } }; @@ -162,8 +164,8 @@ linyaps_box::config::process_t::rlimits_t parse_rlimits(const nlohmann::json &ob return ret; } -linyaps_box::config::linux_t parse_linux(const nlohmann::json &obj, - const nlohmann::json::json_pointer &ptr) +auto parse_linux(const nlohmann::json &obj, const nlohmann::json::json_pointer &ptr) + -> linyaps_box::config::linux_t { auto linux = linyaps_box::config::linux_t{}; if (auto uid_ptr = ptr / "uidMappings"; obj.contains(uid_ptr)) { @@ -280,9 +282,9 @@ linyaps_box::config::linux_t parse_linux(const nlohmann::json &obj, return linux; } -linyaps_box::config parse_1_2_0(const nlohmann::json &j) +auto parse_1_2_0(const nlohmann::json &j) -> linyaps_box::config { - static const auto ptr = ""_json_pointer; + const auto ptr = ""_json_pointer; auto semver = linyaps_box::utils::semver(j[ptr / "ociVersion"].get()); if (!linyaps_box::utils::semver(linyaps_box::config::oci_version).is_compatible_with(semver)) { diff --git a/src/linyaps_box/config.h b/src/linyaps_box/config.h index 3d57b96..00c62f5 100644 --- a/src/linyaps_box/config.h +++ b/src/linyaps_box/config.h @@ -31,7 +31,7 @@ struct config { static constexpr auto oci_version = "1.2.0"; - static config parse(std::istream &is); + static auto parse(std::istream &is) -> config; struct process_t { diff --git a/src/linyaps_box/container.cpp b/src/linyaps_box/container.cpp index c7162ed..14e6142 100644 --- a/src/linyaps_box/container.cpp +++ b/src/linyaps_box/container.cpp @@ -48,6 +48,7 @@ #endif constexpr auto propagations_flag = (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE); +constexpr auto max_symlink_depth{ 32 }; namespace linyaps_box { @@ -112,7 +113,7 @@ std::ostream &operator<<(std::ostream &os, const sync_message message) } break; default: { assert(false); - os << "UNKNOWN " << (uint8_t)message; + os << "UNKNOWN " << static_cast(message); } break; } return os; @@ -120,39 +121,44 @@ std::ostream &operator<<(std::ostream &os, const sync_message message) struct MountFlag { - std::underlying_type_t flag; + unsigned int flag; std::string_view name; }; -constexpr std::array mount_flags{ MountFlag{ MS_RDONLY, "MS_RDONLY" }, - { MS_NOSUID, "MS_NOSUID" }, - { MS_NODEV, "MS_NODEV" }, - { MS_NOEXEC, "MS_NOEXEC" }, - { MS_SYNCHRONOUS, "MS_SYNCHRONOUS" }, - { MS_REMOUNT, "MS_REMOUNT" }, - { MS_MANDLOCK, "MS_MANDLOCK" }, - { MS_DIRSYNC, "MS_DIRSYNC" }, - { LINGYAPS_MS_NOSYMFOLLOW, "MS_NOSYMFOLLOW" }, - { MS_NOATIME, "MS_NOATIME" }, - { MS_NODIRATIME, "MS_NODIRATIME" }, - { MS_BIND, "MS_BIND" }, - { MS_MOVE, "MS_MOVE" }, - { MS_REC, "MS_REC" }, - { MS_SILENT, "MS_SILENT" }, - { MS_POSIXACL, "MS_POSIXACL" }, - { MS_UNBINDABLE, "MS_UNBINDABLE" }, - { MS_PRIVATE, "MS_PRIVATE" }, - { MS_SLAVE, "MS_SLAVE" }, - { MS_SHARED, "MS_SHARED" }, - { MS_RELATIME, "MS_RELATIME" }, - { MS_KERNMOUNT, "MS_KERNMOUNT" }, - { MS_I_VERSION, "MS_I_VERSION" }, - { MS_STRICTATIME, "MS_STRICTATIME" }, - { MS_LAZYTIME, "MS_LAZYTIME" }, - { MS_ACTIVE, "MS_ACTIVE" }, - { MS_NOUSER, "MS_NOUSER" } }; - -std::string dump_mount_flags(uint flags) noexcept +constexpr std::array mount_flags{ + MountFlag{ MS_RDONLY, "MS_RDONLY" }, + { MS_NOSUID, "MS_NOSUID" }, + { MS_NODEV, "MS_NODEV" }, + { MS_NOEXEC, "MS_NOEXEC" }, + { MS_SYNCHRONOUS, "MS_SYNCHRONOUS" }, + { MS_REMOUNT, "MS_REMOUNT" }, + { MS_MANDLOCK, "MS_MANDLOCK" }, + { MS_DIRSYNC, "MS_DIRSYNC" }, + { LINGYAPS_MS_NOSYMFOLLOW, "MS_NOSYMFOLLOW" }, + { MS_NOATIME, "MS_NOATIME" }, + { MS_NODIRATIME, "MS_NODIRATIME" }, + { MS_BIND, "MS_BIND" }, + { MS_MOVE, "MS_MOVE" }, + { MS_REC, "MS_REC" }, + { MS_SILENT, "MS_SILENT" }, + { MS_POSIXACL, "MS_POSIXACL" }, + { MS_UNBINDABLE, "MS_UNBINDABLE" }, + { MS_PRIVATE, "MS_PRIVATE" }, + { MS_SLAVE, "MS_SLAVE" }, + { MS_SHARED, "MS_SHARED" }, + { MS_RELATIME, "MS_RELATIME" }, + { MS_KERNMOUNT, "MS_KERNMOUNT" }, + { MS_I_VERSION, "MS_I_VERSION" }, + { MS_STRICTATIME, "MS_STRICTATIME" }, + { MS_LAZYTIME, "MS_LAZYTIME" }, + { MS_ACTIVE, "MS_ACTIVE" }, + // MS_NOUSER will be overflowed before 2.42.9000 + // refer: + // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=3263675250cbcbbcc76ede4f7c660418bd345a11;hp=cd335350021fd0b7ac533c83717ee38832fd9887 + { static_cast(MS_NOUSER), "MS_NOUSER" } +}; + +[[maybe_unused]] auto dump_mount_flags(unsigned long flags) noexcept -> std::string { std::stringstream ss; ss << "[ "; @@ -282,10 +288,17 @@ void initialize_container(const linyaps_box::config &config, std::ofstream ofs("/proc/self/oom_score_adj"); if (!ofs) { - throw std::runtime_error("failed to open /proc/self/oom_score_adj"); + throw std::system_error(errno, + std::generic_category(), + "failed to open /proc/self/oom_score_adj"); } ofs << score; + if (!ofs) { + throw std::system_error(errno, + std::generic_category(), + "failed to write to /proc/self/oom_score_adj"); + } } } @@ -310,6 +323,7 @@ void syscall_mount(const char *_special_file, if (_dir == nullptr) { return "nullptr"; } + if (auto str = std::string_view{ _dir }; str.rfind(fd_prefix, 0) == 0) { return linyaps_box::utils::inspect_fd(std::stoi(str.data() + fd_prefix.size())); } @@ -367,7 +381,9 @@ void do_remount(const remount_t &mount) } auto state = linyaps_box::utils::statfs(mount.destination_fd); - auto remount_flags = state.f_flags & (MS_NOSUID | MS_NODEV | MS_NOEXEC); + const auto dest_flag = static_cast(state.f_flags); + + auto remount_flags = dest_flag & (MS_NOSUID | MS_NODEV | MS_NOEXEC); if ((remount_flags | mount.flags) != mount.flags) { try { syscall_mount(nullptr, @@ -384,27 +400,46 @@ void do_remount(const remount_t &mount) } } - if ((state.f_flags & MS_RDONLY) != 0) { - remount_flags = state.f_flags & (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RDONLY); + if ((dest_flag & MS_RDONLY) != 0) { + remount_flags = dest_flag & (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RDONLY); syscall_mount(nullptr, destination.c_str(), nullptr, mount.flags | remount_flags, data_ptr); } } -[[nodiscard]] linyaps_box::utils::file_descriptor create_destination_file( +[[nodiscard]] linyaps_box::utils::file_descriptor create_destination_directory( const linyaps_box::utils::file_descriptor &root, const std::filesystem::path &destination) { - LINYAPS_BOX_DEBUG() << "Creating file " << destination.string() << " under " + LINYAPS_BOX_DEBUG() << "Creating directory " << destination.string() << " under " << linyaps_box::utils::inspect_path(root.get()); - const auto &parent = linyaps_box::utils::mkdir(root, destination.parent_path()); - return linyaps_box::utils::touch(parent, destination.filename()); + return linyaps_box::utils::mkdir(root, destination); } -[[nodiscard]] linyaps_box::utils::file_descriptor create_destination_directory( - const linyaps_box::utils::file_descriptor &root, const std::filesystem::path &destination) +[[nodiscard]] linyaps_box::utils::file_descriptor +create_destination_file(const linyaps_box::utils::file_descriptor &root, + const std::filesystem::path &destination, + int max_depth) { - LINYAPS_BOX_DEBUG() << "Creating directory " << destination.string() << " under " + if (max_depth < 0) { + throw std::system_error(ELOOP, std::system_category(), "failed to create file"); + } + + LINYAPS_BOX_DEBUG() << "Creating file " << destination.string() << " under " << linyaps_box::utils::inspect_path(root.get()); - return linyaps_box::utils::mkdir(root, destination); + const auto &parent = create_destination_directory(root, destination.parent_path()); + + try { + auto ret = linyaps_box::utils::touch(parent, + destination.filename(), + O_CLOEXEC | O_CREAT | O_WRONLY | O_NOFOLLOW); + return ret; + } catch (std::system_error &e) { + if (e.code() != std::errc::too_many_symbolic_link_levels) { + throw; + } + + auto target = linyaps_box::utils::readlinkat(parent, destination.filename()); + return create_destination_file(root, target, max_depth - 1); + } } [[nodiscard]] linyaps_box::utils::file_descriptor @@ -412,7 +447,7 @@ create_destination_symlink(const linyaps_box::utils::file_descriptor &root, const std::filesystem::path &source, std::filesystem::path destination) { - auto ret = std::filesystem::read_symlink(source); + auto ret = linyaps_box::utils::readlink(source); auto parent = linyaps_box::utils::mkdir(root, destination.parent_path()); LINYAPS_BOX_DEBUG() << "Creating symlink " << destination.string() << " under " @@ -438,13 +473,8 @@ create_destination_symlink(const linyaps_box::utils::file_descriptor &root, + " already exists and is not a symlink"); } - std::array buf{}; - auto to = ::readlinkat(root.get(), destination.c_str(), buf.data(), buf.size()); - if (to == -1) { - throw std::system_error(errno, std::system_category(), "readlinkat"); - } - - if (std::string_view{ buf.data(), static_cast(to) } == ret) { + auto target = linyaps_box::utils::readlinkat(root, destination); + if (target == ret) { return linyaps_box::utils::open_at(root, destination, O_PATH | O_NOFOLLOW | O_CLOEXEC); } @@ -460,11 +490,9 @@ ensure_mount_destination(bool isDir, const linyaps_box::config::mount_t &mount) try { assert(mount.destination.has_value()); - auto open_flag = O_PATH; - if ((mount.flags & LINGYAPS_MS_NOSYMFOLLOW) != 0) { - open_flag |= O_NOFOLLOW; - } - + auto open_flag = O_PATH | O_CLOEXEC; + LINYAPS_BOX_DEBUG() << "Opening " << (isDir ? "directory " : "file ") + << mount.destination.value() << " under " << root.current_path(); return linyaps_box::utils::open_at(root, mount.destination.value(), open_flag); } catch (const std::system_error &e) { if (e.code().value() != ENOENT) { @@ -483,7 +511,7 @@ try { return create_destination_directory(root, path); } - return create_destination_file(root, path); + return create_destination_file(root, path, max_symlink_depth); } void do_propagation_mount(const linyaps_box::utils::file_descriptor &destination, @@ -523,7 +551,7 @@ void do_propagation_mount(const linyaps_box::utils::file_descriptor &destination nullptr, bind_flags, nullptr); - } catch (const std::system_error &e) { + } catch ([[maybe_unused]] const std::system_error &e) { // mounting sysfs with rootless/userns container will fail with EPERM // TODO: try to bind mount /sys throw; @@ -532,9 +560,9 @@ void do_propagation_mount(const linyaps_box::utils::file_descriptor &destination return linyaps_box::utils::open_at(root, mount.destination.value(), open_flag); } -void do_cgroup_mount([[maybe_unused]] const linyaps_box::utils::file_descriptor &root, - [[maybe_unused]] const linyaps_box::config::mount_t &mount, - [[maybe_unused]] std::string_view unified_cgroup_path) +[[noreturn]] void do_cgroup_mount([[maybe_unused]] const linyaps_box::utils::file_descriptor &root, + [[maybe_unused]] const linyaps_box::config::mount_t &mount, + [[maybe_unused]] std::string_view unified_cgroup_path) { // TODO: implement throw std::runtime_error("mount cgroup: Not implemented"); @@ -651,7 +679,7 @@ class mounter try { do_propagation_mount(rootfsfd, MS_PRIVATE); return; - } catch (const std::system_error &e) { + } catch ([[maybe_unused]] const std::system_error &e) { auto parent_fd = ::openat(rootfsfd.get(), "..", O_PATH | O_CLOEXEC); if (parent_fd < 0) { throw std::system_error(errno, @@ -668,10 +696,10 @@ class mounter } public: - explicit mounter(linyaps_box::utils::file_descriptor root, + explicit mounter(linyaps_box::utils::file_descriptor rootfd, const linyaps_box::container &container) : container(container) - , root(std::move(root)) + , root(std::move(rootfd)) { } @@ -768,6 +796,8 @@ class mounter return; } + LINYAPS_BOX_DEBUG() << "make readonly paths"; + for (const auto &path : *linux->readonly_paths) { linyaps_box::utils::file_descriptor dst; try { @@ -814,10 +844,14 @@ class mounter return; } + LINYAPS_BOX_DEBUG() << "make masked paths"; + for (const auto &path : *linux->masked_paths) { linyaps_box::utils::file_descriptor dst; try { - dst = linyaps_box::utils::open_at(root, path); + // we only need to open a fd to refer to the path + // so O_PATH is sufficient. + dst = linyaps_box::utils::open_at(root, path, O_PATH | O_CLOEXEC); } catch (const std::system_error &e) { if (auto err = e.code().value(); err == ENOENT || err == EACCES) { continue; @@ -1003,14 +1037,16 @@ class mounter void configure_device(const std::filesystem::path &destination, mode_t mode, - int type, + std::filesystem::file_type type, dev_t dev, uid_t uid, gid_t gid) { assert(destination.is_absolute()); - if (type != S_IFCHR && type != S_IFBLK && type != S_IFIFO) { + if (type != std::filesystem::file_type::character + && type != std::filesystem::file_type::block + && type != std::filesystem::file_type::fifo) { throw std::runtime_error("unsupported device type"); } @@ -1026,27 +1062,13 @@ class mounter if (destination_fd.has_value()) { // if already exists, check if it is a required device auto stat = linyaps_box::utils::lstatat(*destination_fd, ""); + auto cur_type = linyaps_box::utils::to_fs_file_type(stat.st_mode); bool satisfied{ true }; - if (__S_ISTYPE(stat.st_mode, type)) { - auto dump_mode = [](mode_t mode) { - if (S_ISCHR(mode)) { - return "Character"; - } - - if (S_ISBLK(mode)) { - return "Block"; - } - - if (S_ISFIFO(mode)) { - return "FIFO"; - } - - return "unknown"; - }; - + if (linyaps_box::utils::is_type(stat.st_mode, type)) { LINYAPS_BOX_DEBUG() << "the type of existing device: " << destination << " is not required\n" - << "expect " << dump_mode(mode) << ", got " << dump_mode(stat.st_mode); + << "expect " << linyaps_box::utils::to_string(type) << ", got " + << linyaps_box::utils::to_string(cur_type); satisfied = false; } @@ -1076,7 +1098,9 @@ class mounter try { auto path = destination.relative_path(); - linyaps_box::utils::mknodat(root, path, mode | type, dev); + auto f_type = static_cast(linyaps_box::utils::to_linux_file_type(type)); + + linyaps_box::utils::mknodat(root, path, mode | f_type, dev); auto new_dev = linyaps_box::utils::open_at(root, path, O_PATH); path = new_dev.proc_path(); @@ -1109,14 +1133,16 @@ class mounter LINYAPS_BOX_DEBUG() << "Configure default devices"; constexpr auto default_mode = 0666; + constexpr auto default_type = std::filesystem::file_type::character; auto uid = container.get_config().process.user.uid; auto gid = container.get_config().process.user.gid; - this->configure_device("/dev/null", default_mode, S_IFCHR, makedev(1, 3), uid, gid); - this->configure_device("/dev/zero", default_mode, S_IFCHR, makedev(1, 5), uid, gid); - this->configure_device("/dev/full", default_mode, S_IFCHR, makedev(1, 7), uid, gid); - this->configure_device("/dev/random", default_mode, S_IFCHR, makedev(1, 8), uid, gid); - this->configure_device("/dev/urandom", default_mode, S_IFCHR, makedev(1, 9), uid, gid); - this->configure_device("/dev/tty", default_mode, S_IFCHR, makedev(5, 0), uid, gid); + + this->configure_device("/dev/null", default_mode, default_type, makedev(1, 3), uid, gid); + this->configure_device("/dev/zero", default_mode, default_type, makedev(1, 5), uid, gid); + this->configure_device("/dev/full", default_mode, default_type, makedev(1, 7), uid, gid); + this->configure_device("/dev/random", default_mode, default_type, makedev(1, 8), uid, gid); + this->configure_device("/dev/urandom", default_mode, default_type, makedev(1, 9), uid, gid); + this->configure_device("/dev/tty", default_mode, default_type, makedev(5, 0), uid, gid); // bind mount /dev/pts/ptmx to /dev/ptmx // https://docs.kernel.org/filesystems/devpts.html @@ -1212,16 +1238,19 @@ void configure_mounts(const linyaps_box::container &container, const std::filesy LINYAPS_BOX_DEBUG() << "Execute container process:" << [&process]() -> std::string { std::stringstream ss; + assert(!process.args.empty()); + ss << " " << process.args[0]; - for (size_t i = 1; i < process.args.size(); ++i) { - ss << " " << process.args[i]; - } + std::for_each(process.args.cbegin() + 1, process.args.cend(), [&ss](const auto &arg) { + ss << " " << arg; + }); + return ss.str(); }(); execvpe(c_args[0], - const_cast(c_args.data()), - const_cast(c_env.data())); + const_cast(c_args.data()), // NOLINT + const_cast(c_env.data())); // NOLINT throw std::system_error(errno, std::generic_category(), "execvpe"); } @@ -1489,7 +1518,7 @@ void set_capabilities(const linyaps_box::container &container, int last_cap) throw std::system_error(errno, std::generic_category(), "cap_ambient_clear_all"); } - std::for_each(capabilities.ambient.cend(), capabilities.ambient.cend(), [](cap_value_t cap) { + std::for_each(capabilities.ambient.cbegin(), capabilities.ambient.cend(), [](cap_value_t cap) { auto ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0L, 0L); if (ret < 0) { throw std::system_error(errno, std::generic_category(), "cap_ambient_raise"); @@ -1562,8 +1591,13 @@ void processing_extensions(const linyaps_box::container &container) LINYAPS_BOX_DEBUG() << "Processing container extensions"; // ext_ns_last_pid - if (auto it = config.annotations->find("cn.org.linyaps.runtime.ns_last_pid"); - it != config.annotations->end()) { + // This file may not exist if the kernel config CONFIG_CHECKPOINT_RESTORE is not enabled + // and this feature originally was used for userspace checkpoint/restore + // we use this feature for avoiding two process has the same pid. + // e.g some application will register a tray through dbus and use the pid as the part of + // dbus object path, if two process has the same pid, the dbus object path will conflict + auto it = config.annotations->find("cn.org.linyaps.runtime.ns_last_pid"); + while (it != config.annotations->end()) { LINYAPS_BOX_DEBUG() << "Processing ns_last_pid extension: " << it->second; // Validate input is a valid pid_t number @@ -1576,23 +1610,33 @@ void processing_extensions(const linyaps_box::container &container) + std::to_string(std::numeric_limits::max()) + ")"); } } catch (const std::out_of_range &e) { - throw std::runtime_error("ns_last_pid value out of range: " + it->second); + throw std::runtime_error("parse ns_last_pid " + it->second + " failed: " + e.what()); } catch (const std::invalid_argument &e) { - throw std::runtime_error("Invalid ns_last_pid value: " + it->second - + " (must be a valid number)"); + throw std::runtime_error("parse ns_last_pid " + it->second + " failed: " + e.what()); + } + + // ignore ns_last_pid if the file does not exist + auto ns_last_pid = std::filesystem::path{ "/proc/sys/kernel/ns_last_pid" }; + if (!std::filesystem::exists(ns_last_pid)) { + break; } - std::ofstream ofs("/proc/sys/kernel/ns_last_pid"); + std::ofstream ofs(ns_last_pid); if (!ofs) { - throw std::runtime_error("failed to open /proc/sys/kernel/ns_last_pid"); + throw std::system_error(errno, + std::generic_category(), + "failed to open /proc/sys/kernel/ns_last_pid"); } ofs << it->second; if (!ofs) { - throw std::runtime_error("failed to write to /proc/sys/kernel/ns_last_pid"); + throw std::system_error(errno, + std::generic_category(), + "failed to write to /proc/sys/kernel/ns_last_pid"); } LINYAPS_BOX_DEBUG() << "Successfully set ns_last_pid to " << it->second; + break; } LINYAPS_BOX_DEBUG() << "Container extensions processing completed"; @@ -1628,8 +1672,16 @@ try { auto &args = *static_cast(data); assert(args.socket.get() >= 0); - close_other_fds( - { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, (unsigned int)(args.socket.get()) }); + std::set except_fds{ + STDIN_FILENO, + STDOUT_FILENO, + STDERR_FILENO, + }; + for (auto fd = 0; fd < args.container->preserve_fds(); ++fd) { + except_fds.insert(fd + 3); + } + except_fds.insert(static_cast(args.socket.get())); + close_other_fds(std::move(except_fds)); const auto &container = *args.container; const auto &process = *args.process; @@ -1637,6 +1689,7 @@ try { auto rootfs = container.get_config().root.path; if (rootfs.is_relative()) { + LINYAPS_BOX_DEBUG() << "rootfs is relative based on bundle path:" << container.get_bundle(); rootfs = std::filesystem::canonical(container.get_bundle() / rootfs); } @@ -1709,8 +1762,11 @@ namespace runtime_ns { flag |= CLONE_NEWCGROUP; LINYAPS_BOX_DEBUG() << "Add CLONE_NEWCGROUP, flag=0x" << std::hex << flag; } break; + case linyaps_box::config::linux_t::namespace_t::INVALID: { + throw std::invalid_argument("invalid namespace type: " + std::to_string(ns.type)); + } break; default: { - throw std::invalid_argument("invalid namespace"); + throw std::invalid_argument("unknown namespace type: " + std::to_string(ns.type)); } } @@ -1756,12 +1812,12 @@ class child_stack assert(false); } - [[nodiscard]] void *top() const noexcept + [[nodiscard]] auto top() const noexcept -> void * { if constexpr (LINYAPS_BOX_STACK_GROWTH_DOWN) { - return (char *)this->stack_low + LINYAPS_BOX_CLONE_CHILD_STACK_SIZE; + return static_cast(this->stack_low) + LINYAPS_BOX_CLONE_CHILD_STACK_SIZE; } else { - return (char *)this->stack_low - LINYAPS_BOX_CLONE_CHILD_STACK_SIZE; + return static_cast(this->stack_low) - LINYAPS_BOX_CLONE_CHILD_STACK_SIZE; } } @@ -1804,14 +1860,14 @@ std::tuple start_container_process( namespaces = config.linux->namespaces; } - int clone_flag = runtime_ns::generate_clone_flag(namespaces); + const int clone_flag = runtime_ns::generate_clone_flag(namespaces); clone_fn_args args = { &container, &process, std::move(sockets.second) }; LINYAPS_BOX_DEBUG() << "OCI runtime in runtime namespace: PID=" << getpid() << " PIDNS=" << linyaps_box::utils::get_pid_namespace(); - child_stack stack; - int child_pid = clone(container_ns::clone_fn, stack.top(), clone_flag, (void *)&args); + const child_stack stack; + const int child_pid = clone(container_ns::clone_fn, stack.top(), clone_flag, (void *)&args); if (child_pid < 0) { throw std::runtime_error("clone failed"); } @@ -2274,14 +2330,13 @@ void poststop_hooks(const linyaps_box::container &container) noexcept } // namespace linyaps_box::container::container(const status_directory &status_dir, - const std::string &id, - const std::filesystem::path &bundle, - std::filesystem::path config, - cgroup_manager_t manager) - : container_ref(status_dir, id) - , bundle(bundle) + const create_container_options_t &options) + : container_ref(status_dir, options.ID) + , preserve_fds_(options.preserve_fds) , data(new linyaps_box::container_data) + , bundle(options.bundle) { + auto config = options.config; if (config.is_relative()) { config = bundle / config; } @@ -2307,7 +2362,7 @@ linyaps_box::container::container(const status_directory &status_dir, { container_status_t status; status.oci_version = linyaps_box::config::oci_version; - status.ID = id; + status.ID = options.ID; status.PID = getpid(); status.status = container_status_t::runtime_status::CREATING; status.bundle = bundle; @@ -2320,7 +2375,7 @@ linyaps_box::container::container(const status_directory &status_dir, this->status_dir().write(status); } - switch (manager) { + switch (options.manager) { case cgroup_manager_t::disabled: { this->manager = std::make_unique(); } break; diff --git a/src/linyaps_box/container.h b/src/linyaps_box/container.h index 420d7b5..5a8c638 100644 --- a/src/linyaps_box/container.h +++ b/src/linyaps_box/container.h @@ -13,34 +13,47 @@ namespace linyaps_box { struct container_data; +struct create_container_options_t +{ + cgroup_manager_t manager; + int preserve_fds; + std::string ID; + std::filesystem::path bundle; + std::filesystem::path config; +}; + class container final : public container_ref { public: - container(const status_directory &status_dir, - const std::string &id, - const std::filesystem::path &bundle, - std::filesystem::path config, - cgroup_manager_t manager); - - [[nodiscard]] const linyaps_box::config &get_config() const; - [[nodiscard]] const std::filesystem::path &get_bundle() const; - [[nodiscard]] int run(const config::process_t &process) const; + container(const status_directory &status_dir, const create_container_options_t &options); + + container(const container &) = delete; + auto operator=(const container &) -> container & = delete; + container(container &&) = delete; + auto operator=(container &&) -> container & = delete; + + [[nodiscard]] auto get_config() const -> const linyaps_box::config &; + [[nodiscard]] auto get_bundle() const -> const std::filesystem::path &; + [[nodiscard]] auto run(const config::process_t &process) const -> int; // TODO:: support fully container capabilities, e.g. create, start, stop, delete... - friend container_data &get_private_data(const container &c) noexcept; + friend auto get_private_data(const container &c) noexcept -> container_data &; ~container() noexcept override; - [[nodiscard]] auto host_gid() const noexcept { return host_gid_; }; + [[nodiscard]] auto host_gid() const noexcept { return host_gid_; } [[nodiscard]] auto host_uid() const noexcept { return host_uid_; } + [[nodiscard]] auto preserve_fds() const noexcept { return preserve_fds_; } + private: void cgroup_preenter(const cgroup_options &options, utils::file_descriptor &dirfd); - std::filesystem::path bundle; - linyaps_box::config config; - std::unique_ptr manager; + int preserve_fds_; gid_t host_gid_; uid_t host_uid_; container_data *data{ nullptr }; + std::filesystem::path bundle; + std::unique_ptr manager; + linyaps_box::config config; }; } // namespace linyaps_box diff --git a/src/linyaps_box/container_ref.cpp b/src/linyaps_box/container_ref.cpp index 48a84f9..45a0696 100644 --- a/src/linyaps_box/container_ref.cpp +++ b/src/linyaps_box/container_ref.cpp @@ -12,14 +12,16 @@ #include linyaps_box::container_ref::container_ref(const status_directory &status_dir, std::string id) - : id(std::move(id)) + : id_(std::move(id)) , status_dir_(status_dir) { } +linyaps_box::container_ref::~container_ref() noexcept = default; + linyaps_box::container_status_t linyaps_box::container_ref::status() const { - return this->status_dir_.read(this->id); + return this->status_dir_.read(this->id_); } void linyaps_box::container_ref::kill(int signal) const @@ -111,5 +113,5 @@ const linyaps_box::status_directory &linyaps_box::container_ref::status_dir() co const std::string &linyaps_box::container_ref::get_id() const { - return this->id; + return this->id_; } diff --git a/src/linyaps_box/container_ref.h b/src/linyaps_box/container_ref.h index beb9fd3..dfa69b2 100644 --- a/src/linyaps_box/container_ref.h +++ b/src/linyaps_box/container_ref.h @@ -14,18 +14,23 @@ class container_ref { public: container_ref(const status_directory &status_dir, std::string id); - virtual ~container_ref() noexcept = default; + virtual ~container_ref() noexcept; - [[nodiscard]] container_status_t status() const; + container_ref(const container_ref &) = delete; + auto operator=(const container_ref &) -> container_ref & = delete; + container_ref(container_ref &&) = delete; + auto operator=(container_ref &&) -> container_ref & = delete; + + [[nodiscard]] auto status() const -> container_status_t; void kill(int signal) const; [[noreturn]] void exec(const config::process_t &process); protected: - [[nodiscard]] const status_directory &status_dir() const; - [[nodiscard]] const std::string &get_id() const; + [[nodiscard]] auto status_dir() const -> const status_directory &; + [[nodiscard]] auto get_id() const -> const std::string &; private: - std::string id; + std::string id_; const status_directory &status_dir_; }; diff --git a/src/linyaps_box/container_status.cpp b/src/linyaps_box/container_status.cpp index f3fc47f..102556a 100644 --- a/src/linyaps_box/container_status.cpp +++ b/src/linyaps_box/container_status.cpp @@ -5,7 +5,7 @@ #include "linyaps_box/container_status.h" namespace linyaps_box { -std::string to_string(linyaps_box::container_status_t::runtime_status status) +auto to_string(linyaps_box::container_status_t::runtime_status status) -> std::string { switch (status) { case linyaps_box::container_status_t::runtime_status::CREATING: @@ -16,12 +16,12 @@ std::string to_string(linyaps_box::container_status_t::runtime_status status) return "running"; case linyaps_box::container_status_t::runtime_status::STOPPED: return "stopped"; + default: + throw std::logic_error("unknown status"); } - - throw std::logic_error("unknown status"); } -linyaps_box::container_status_t::runtime_status from_string(std::string_view status) +auto from_string(std::string_view status) -> linyaps_box::container_status_t::runtime_status { if (status == "creating") { return linyaps_box::container_status_t::runtime_status::CREATING; @@ -39,7 +39,7 @@ linyaps_box::container_status_t::runtime_status from_string(std::string_view sta throw std::logic_error("unknown status"); } -nlohmann::json status_to_json(const linyaps_box::container_status_t &status) +auto status_to_json(const linyaps_box::container_status_t &status) -> nlohmann::json { return nlohmann::json::object({ { "id", status.ID }, { "pid", status.PID }, diff --git a/src/linyaps_box/container_status.h b/src/linyaps_box/container_status.h index 71cce0c..4211d16 100644 --- a/src/linyaps_box/container_status.h +++ b/src/linyaps_box/container_status.h @@ -26,8 +26,8 @@ struct container_status_t std::unordered_map annotations; }; -std::string to_string(linyaps_box::container_status_t::runtime_status status); -linyaps_box::container_status_t::runtime_status from_string(std::string_view status); -nlohmann::json status_to_json(const linyaps_box::container_status_t &status); +auto to_string(linyaps_box::container_status_t::runtime_status status) -> std::string; +auto from_string(std::string_view status) -> linyaps_box::container_status_t::runtime_status; +auto status_to_json(const linyaps_box::container_status_t &status) -> nlohmann::json; } // namespace linyaps_box diff --git a/src/linyaps_box/impl/disabled_cgroup_manager.cpp b/src/linyaps_box/impl/disabled_cgroup_manager.cpp index 5150c1b..4d12d33 100644 --- a/src/linyaps_box/impl/disabled_cgroup_manager.cpp +++ b/src/linyaps_box/impl/disabled_cgroup_manager.cpp @@ -5,15 +5,16 @@ #include "linyaps_box/impl/disabled_cgroup_manager.h" namespace linyaps_box { -[[nodiscard]] cgroup_manager_t disabled_cgroup_manager::type() const +[[nodiscard]] auto disabled_cgroup_manager::type() const -> cgroup_manager_t { return cgroup_manager_t::disabled; } -cgroup_status disabled_cgroup_manager::create_cgroup([[maybe_unused]] const cgroup_options &options) +auto disabled_cgroup_manager::create_cgroup([[maybe_unused]] const cgroup_options &options) + -> cgroup_status { cgroup_status status{}; - set_manager_type(status, type()); + set_manager(status, type()); return status; } diff --git a/src/linyaps_box/impl/disabled_cgroup_manager.h b/src/linyaps_box/impl/disabled_cgroup_manager.h index db3d0a2..2c66070 100644 --- a/src/linyaps_box/impl/disabled_cgroup_manager.h +++ b/src/linyaps_box/impl/disabled_cgroup_manager.h @@ -11,9 +11,9 @@ namespace linyaps_box { class disabled_cgroup_manager : public virtual cgroup_manager { public: - [[nodiscard]] cgroup_manager_t type() const override; + [[nodiscard]] auto type() const -> cgroup_manager_t override; - cgroup_status create_cgroup([[maybe_unused]] const cgroup_options &options) override; + auto create_cgroup([[maybe_unused]] const cgroup_options &options) -> cgroup_status override; void precreate_cgroup([[maybe_unused]] const cgroup_options &options, [[maybe_unused]] utils::file_descriptor &dirfd) override; diff --git a/src/linyaps_box/impl/status_directory.cpp b/src/linyaps_box/impl/status_directory.cpp index 7d5a5a3..961c79c 100644 --- a/src/linyaps_box/impl/status_directory.cpp +++ b/src/linyaps_box/impl/status_directory.cpp @@ -9,15 +9,15 @@ #include "linyaps_box/utils/log.h" #include "nlohmann/json.hpp" -#include -#include +#include // IWYU pragma: keep #include +#include #include namespace { -linyaps_box::container_status_t read_status(const std::filesystem::path &path) +auto read_status(const std::filesystem::path &path) -> linyaps_box::container_status_t { nlohmann::json j; { @@ -34,7 +34,7 @@ linyaps_box::container_status_t read_status(const std::filesystem::path &path) ret.PID = j["pid"]; ret.ID = j["id"]; ret.status = linyaps_box::from_string(j["status"].get()); - if (kill(ret.PID, 0) != 0) { + if (::kill(ret.PID, 0) != 0) { ret.status = linyaps_box::container_status_t::runtime_status::STOPPED; } ret.bundle = std::string(j["bundle"]); @@ -61,22 +61,22 @@ void linyaps_box::impl::status_directory::write(const container_status_t &status utils::atomic_write(this->path / (status.ID + ".json"), j.dump()); } -linyaps_box::container_status_t -linyaps_box::impl::status_directory::read(const std::string &id) const +auto linyaps_box::impl::status_directory::read(const std::string &id) const + -> linyaps_box::container_status_t { return read_status(this->path / (id + ".json")); } void linyaps_box::impl::status_directory::remove(const std::string &id) const { - auto path = this->path / (id + ".json"); - LINYAPS_BOX_DEBUG() << "Remove " << path; - if (!std::filesystem::remove(path)) { - LINYAPS_BOX_WARNING() << "Failed to remove " << path; + auto file_path = this->path / (id + ".json"); + LINYAPS_BOX_DEBUG() << "Remove " << file_path; + if (!std::filesystem::remove(file_path)) { + LINYAPS_BOX_WARNING() << "Failed to remove " << file_path; } } -std::vector linyaps_box::impl::status_directory::list() const +auto linyaps_box::impl::status_directory::list() const -> std::vector { std::vector ret; for (const auto &entry : std::filesystem::directory_iterator(this->path)) { @@ -95,8 +95,8 @@ std::vector linyaps_box::impl::status_directory::list() const return ret; } -linyaps_box::impl::status_directory::status_directory(const std::filesystem::path &path) - : path(path) +linyaps_box::impl::status_directory::status_directory(std::filesystem::path dir) + : path(std::move(dir)) { if (std::filesystem::is_directory(path) || std::filesystem::create_directories(path)) { return; diff --git a/src/linyaps_box/impl/status_directory.h b/src/linyaps_box/impl/status_directory.h index 22dd241..bd10185 100644 --- a/src/linyaps_box/impl/status_directory.h +++ b/src/linyaps_box/impl/status_directory.h @@ -14,11 +14,11 @@ class status_directory : public virtual linyaps_box::status_directory { public: void write(const container_status_t &status) const override; - [[nodiscard]] container_status_t read(const std::string &id) const override; + [[nodiscard]] auto read(const std::string &id) const -> container_status_t override; void remove(const std::string &id) const override; - [[nodiscard]] std::vector list() const override; + [[nodiscard]] auto list() const -> std::vector override; - explicit status_directory(const std::filesystem::path &path); + explicit status_directory(std::filesystem::path dir); private: std::filesystem::path path; diff --git a/src/linyaps_box/impl/table_printer.cpp b/src/linyaps_box/impl/table_printer.cpp index 5576148..a06d818 100644 --- a/src/linyaps_box/impl/table_printer.cpp +++ b/src/linyaps_box/impl/table_printer.cpp @@ -7,7 +7,7 @@ #include namespace { -std::string get_status_string(const linyaps_box::container_status_t::runtime_status &status) +auto get_status_string(const linyaps_box::container_status_t::runtime_status &status) -> std::string { switch (status) { case linyaps_box::container_status_t::runtime_status::CREATING: @@ -18,17 +18,17 @@ std::string get_status_string(const linyaps_box::container_status_t::runtime_sta return "running"; case linyaps_box::container_status_t::runtime_status::STOPPED: return "stopped"; + default: + throw std::logic_error("unknown status"); } - - throw std::logic_error("unknown status"); } } // namespace void linyaps_box::impl::table_printer::print_statuses(const std::vector &status) { - size_t max_length = 4; + int max_length = 4; for (const auto &s : status) { - max_length = std::max(max_length, s.ID.length()); + max_length = std::max(max_length, static_cast(s.ID.length())); } std::cout << std::left << std::setw(max_length + 1) << "NAME" << std::setw(10) << "PID" diff --git a/src/linyaps_box/interface.cpp b/src/linyaps_box/interface.cpp index 2647a7b..44e6431 100644 --- a/src/linyaps_box/interface.cpp +++ b/src/linyaps_box/interface.cpp @@ -3,3 +3,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "linyaps_box/interface.h" + +namespace linyaps_box { + +// ensure vtable only here +interface::~interface() = default; + +} // namespace linyaps_box diff --git a/src/linyaps_box/interface.h b/src/linyaps_box/interface.h index bb3f550..cd42b45 100644 --- a/src/linyaps_box/interface.h +++ b/src/linyaps_box/interface.h @@ -11,10 +11,10 @@ class interface { public: interface() = default; - virtual ~interface() = default; + virtual ~interface(); interface(const interface &) = delete; - interface &operator=(const interface &) = delete; + auto operator=(const interface &) -> interface & = delete; interface(interface &&) = delete; - interface &operator=(interface &&) = delete; + auto operator=(interface &&) -> interface & = delete; }; } // namespace linyaps_box diff --git a/src/linyaps_box/printer.cpp b/src/linyaps_box/printer.cpp index 4a3340e..456ebab 100644 --- a/src/linyaps_box/printer.cpp +++ b/src/linyaps_box/printer.cpp @@ -3,3 +3,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "linyaps_box/printer.h" + +namespace linyaps_box { + +printer::~printer() = default; + +} // namespace linyaps_box diff --git a/src/linyaps_box/printer.h b/src/linyaps_box/printer.h index 5a295da..6a82919 100644 --- a/src/linyaps_box/printer.h +++ b/src/linyaps_box/printer.h @@ -12,7 +12,17 @@ namespace linyaps_box { class printer : public virtual interface { +protected: + printer() = default; + public: + ~printer() override; + + printer(const printer &) = delete; + auto operator=(const printer &) -> printer & = delete; + printer(printer &&) = delete; + auto operator=(printer &&) -> printer & = delete; + virtual void print_status(const container_status_t &status) = 0; virtual void print_statuses(const std::vector &status) = 0; }; diff --git a/src/linyaps_box/runtime.cpp b/src/linyaps_box/runtime.cpp index 93ae356..3f88027 100644 --- a/src/linyaps_box/runtime.cpp +++ b/src/linyaps_box/runtime.cpp @@ -5,27 +5,30 @@ #include "linyaps_box/runtime.h" linyaps_box::runtime_t::runtime_t(std::unique_ptr &&status_dir) - : status_dir{ std::move(status_dir) } + : status_dir_{ std::move(status_dir) } { - if (!this->status_dir) { + if (!this->status_dir_) { throw std::invalid_argument("status_dir is nullptr"); } } -std::unordered_map linyaps_box::runtime_t::containers() +auto linyaps_box::runtime_t::containers() + -> std::unordered_map { - auto container_ids = this->status_dir->list(); + auto container_ids = this->status_dir_->list(); std::unordered_map containers; for (const auto &container_id : container_ids) { - containers.emplace(container_id, container_ref(*this->status_dir, container_id)); + containers.emplace(std::piecewise_construct, + std::forward_as_tuple(container_id), + std::forward_as_tuple(*this->status_dir_, container_id)); } return containers; } -linyaps_box::container linyaps_box::runtime_t::create_container( - const linyaps_box::runtime_t::create_container_options_t &options) +auto linyaps_box::runtime_t::create_container(const create_container_options_t &options) + -> linyaps_box::container { - return { *this->status_dir, options.ID, options.bundle, options.config, options.manager }; + return { *this->status_dir_, options }; } diff --git a/src/linyaps_box/runtime.h b/src/linyaps_box/runtime.h index f55e27e..a219b65 100644 --- a/src/linyaps_box/runtime.h +++ b/src/linyaps_box/runtime.h @@ -17,20 +17,12 @@ class runtime_t { public: explicit runtime_t(std::unique_ptr &&status_dir); - std::unordered_map containers(); + auto containers() -> std::unordered_map; - struct create_container_options_t - { - std::filesystem::path bundle; - std::filesystem::path config; - std::string ID; - cgroup_manager_t manager; - }; - - container create_container(const create_container_options_t &options); + auto create_container(const create_container_options_t &options) -> container; private: - std::unique_ptr status_dir; + std::unique_ptr status_dir_; }; } // namespace linyaps_box diff --git a/src/linyaps_box/status_directory.cpp b/src/linyaps_box/status_directory.cpp index 98eb590..d281230 100644 --- a/src/linyaps_box/status_directory.cpp +++ b/src/linyaps_box/status_directory.cpp @@ -3,3 +3,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "linyaps_box/status_directory.h" + +namespace linyaps_box { +status_directory::~status_directory() = default; +} // namespace linyaps_box diff --git a/src/linyaps_box/status_directory.h b/src/linyaps_box/status_directory.h index 74db51f..10d53f0 100644 --- a/src/linyaps_box/status_directory.h +++ b/src/linyaps_box/status_directory.h @@ -12,10 +12,20 @@ namespace linyaps_box { class status_directory : public virtual interface { +protected: + status_directory() = default; + public: + ~status_directory() override; + + status_directory(const status_directory &) = delete; + auto operator=(const status_directory &) -> status_directory & = delete; + status_directory(status_directory &&) = delete; + auto operator=(status_directory &&) -> status_directory & = delete; + virtual void write(const container_status_t &status) const = 0; - [[nodiscard]] virtual container_status_t read(const std::string &id) const = 0; + [[nodiscard]] virtual auto read(const std::string &id) const -> container_status_t = 0; virtual void remove(const std::string &id) const = 0; - [[nodiscard]] virtual std::vector list() const = 0; + [[nodiscard]] virtual auto list() const -> std::vector = 0; }; } // namespace linyaps_box diff --git a/src/linyaps_box/utils/cgroups.cpp b/src/linyaps_box/utils/cgroups.cpp index f13fee3..311ac62 100644 --- a/src/linyaps_box/utils/cgroups.cpp +++ b/src/linyaps_box/utils/cgroups.cpp @@ -11,7 +11,7 @@ constexpr auto cgroup_root = "/sys/fs/cgroups"; -linyaps_box::utils::cgroup_t linyaps_box::utils::get_cgroup_type() +auto linyaps_box::utils::get_cgroup_type() -> linyaps_box::utils::cgroup_t { static auto cgroup_type = []() -> cgroup_t { struct statfs stat{}; diff --git a/src/linyaps_box/utils/cgroups.h b/src/linyaps_box/utils/cgroups.h index 98cbf16..fa8be44 100644 --- a/src/linyaps_box/utils/cgroups.h +++ b/src/linyaps_box/utils/cgroups.h @@ -8,8 +8,8 @@ namespace linyaps_box::utils { -enum class cgroup_t : std::uint16_t { unified, legacy, hybrid }; +enum class cgroup_t : std::uint8_t { unified, legacy, hybrid }; -cgroup_t get_cgroup_type(); +auto get_cgroup_type() -> cgroup_t; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/close_range.cpp b/src/linyaps_box/utils/close_range.cpp index dd2ba86..71cb6cb 100644 --- a/src/linyaps_box/utils/close_range.cpp +++ b/src/linyaps_box/utils/close_range.cpp @@ -24,7 +24,7 @@ void syscall_close_range(uint fd, uint max_fd, int flags) void close_range_fallback(uint first, uint last, int flags) { - if ((flags & CLOSE_RANGE_UNSHARE) != 0) { + if ((static_cast(flags) & CLOSE_RANGE_UNSHARE) != 0) { // not support CLOSE_RANGE_UNSHARE // it request to create a new file descriptor table // we can't do that in user space @@ -59,7 +59,7 @@ void close_range_fallback(uint first, uint last, int flags) continue; } - auto fd = std::stoi(name.c_str()); + auto fd = std::stoi(name); if (fd == self_fd) { continue; } @@ -68,7 +68,7 @@ void close_range_fallback(uint first, uint last, int flags) continue; } - if ((flags & CLOSE_RANGE_CLOEXEC) != 0) { + if ((static_cast(flags) & CLOSE_RANGE_CLOEXEC) != 0) { if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { throw std::system_error(errno, std::generic_category(), @@ -89,10 +89,10 @@ void linyaps_box::utils::close_range(uint first, uint last, int flags) << [flags]() -> std::string { std::stringstream ss; ss << '['; - if ((flags & CLOSE_RANGE_CLOEXEC) != 0) { + if ((static_cast(flags) & CLOSE_RANGE_CLOEXEC) != 0) { ss << "CLOSE_RANGE_CLOEXEC "; } - if ((flags & CLOSE_RANGE_UNSHARE) != 0) { + if ((static_cast(flags) & CLOSE_RANGE_UNSHARE) != 0) { ss << "CLOSE_RANGE_UNSHARE "; } ss << ']'; diff --git a/src/linyaps_box/utils/defer.h b/src/linyaps_box/utils/defer.h index 7bee2e1..0bdc5d9 100644 --- a/src/linyaps_box/utils/defer.h +++ b/src/linyaps_box/utils/defer.h @@ -26,16 +26,15 @@ template defer & = delete; defer(defer &&other) noexcept - : fn(std::move(other.fn)) + : fn_(std::move(other.fn_)) , cancelled(other.cancelled) { other.cancelled = true; @@ -51,7 +50,7 @@ struct defer execute(); } - fn = std::move(other.fn); + fn_ = std::move(other.fn_); cancelled = other.cancelled; other.cancelled = true; @@ -67,22 +66,22 @@ struct defer void cancel() noexcept { cancelled = true; } - [[nodiscard]] bool is_cancelled() const noexcept { return cancelled; } + [[nodiscard]] auto is_cancelled() const noexcept -> bool { return cancelled; } private: void execute() noexcept { if constexpr (Policy == defer_policy::always) { - fn(); + fn_(); } else if constexpr (Policy == defer_policy::on_error) { if (std::uncaught_exceptions() > 0) { - fn(); + fn_(); } } } - Fn fn; - bool cancelled; + Fn fn_; + bool cancelled{ false }; }; // Helper functions to create defer objects diff --git a/src/linyaps_box/utils/file_describer.cpp b/src/linyaps_box/utils/file_describer.cpp index 51309a8..c7e9983 100644 --- a/src/linyaps_box/utils/file_describer.cpp +++ b/src/linyaps_box/utils/file_describer.cpp @@ -15,19 +15,31 @@ linyaps_box::utils::file_descriptor_closed_exception::file_descriptor_closed_exc { } +linyaps_box::utils::file_descriptor_closed_exception::~file_descriptor_closed_exception() noexcept = + default; + +linyaps_box::utils::file_descriptor_invalid_exception::file_descriptor_invalid_exception( + const std::string &message) + : std::runtime_error(message) +{ +} + +linyaps_box::utils::file_descriptor_invalid_exception:: + ~file_descriptor_invalid_exception() noexcept = default; + linyaps_box::utils::file_descriptor::file_descriptor(int fd) - : fd(fd) + : fd_(fd) { } linyaps_box::utils::file_descriptor::~file_descriptor() { - if (fd == -1) { + if (fd_ < 0) { return; } - if (close(fd) != 0) { - LINYAPS_BOX_ERR() << "close " << fd << " failed:" << ::strerror(errno); + if (close(fd_) != 0) { + LINYAPS_BOX_ERR() << "close " << fd_ << " failed:" << ::strerror(errno); } } @@ -36,33 +48,41 @@ linyaps_box::utils::file_descriptor::file_descriptor(file_descriptor &&other) no *this = std::move(other); } -int linyaps_box::utils::file_descriptor::release() && noexcept +auto linyaps_box::utils::file_descriptor::release() -> void { int ret = -1; - std::swap(ret, fd); + std::swap(ret, fd_); - return ret; + if (::close(ret) < 0) { + auto msg{ "failed to close file descriptor " + std::to_string(ret) + ": " + + ::strerror(errno) }; + throw file_descriptor_invalid_exception(msg); + } } -int linyaps_box::utils::file_descriptor::get() const noexcept +auto linyaps_box::utils::file_descriptor::get() const noexcept -> int { - return fd; + return fd_; } -linyaps_box::utils::file_descriptor linyaps_box::utils::file_descriptor::duplicate() const +auto linyaps_box::utils::file_descriptor::duplicate() const -> linyaps_box::utils::file_descriptor { - if (fd == -1) { + if (fd_ == -1) { throw file_descriptor_closed_exception(); } - auto ret = dup(fd); + if (fd_ == AT_FDCWD) { + throw file_descriptor_invalid_exception("cannot duplicate AT_FDCWD"); + } + + auto ret = dup(fd_); if (ret < 0) { throw std::system_error(errno, std::generic_category(), "fcntl"); } // dup will lost the close-on-exec flag // and we don't want to use FD_DUPFD_CLOEXEC due to it require a specific fd number - auto flag = fcntl(fd, F_GETFD); + auto flag = fcntl(fd_, F_GETFD); if (flag < 0) { throw std::system_error(errno, std::generic_category(), "fcntl"); } @@ -74,11 +94,11 @@ linyaps_box::utils::file_descriptor linyaps_box::utils::file_descriptor::duplica return file_descriptor{ ret }; } -linyaps_box::utils::file_descriptor & -linyaps_box::utils::file_descriptor::operator<<(const std::byte &byte) +auto linyaps_box::utils::file_descriptor::operator<<(const std::byte &byte) + -> linyaps_box::utils::file_descriptor & { while (true) { - auto ret = write(fd, &byte, 1); + auto ret = write(fd_, &byte, 1); if (ret == 1) { return *this; } @@ -89,18 +109,18 @@ linyaps_box::utils::file_descriptor::operator<<(const std::byte &byte) } } -linyaps_box::utils::file_descriptor & -linyaps_box::utils::file_descriptor::operator=(file_descriptor &&other) noexcept +auto linyaps_box::utils::file_descriptor::operator=(file_descriptor &&other) noexcept + -> linyaps_box::utils::file_descriptor & { - std::swap(this->fd, other.fd); + std::swap(this->fd_, other.fd_); return *this; } -linyaps_box::utils::file_descriptor & -linyaps_box::utils::file_descriptor::operator>>(std::byte &byte) +auto linyaps_box::utils::file_descriptor::operator>>(std::byte &byte) + -> linyaps_box::utils::file_descriptor & { while (true) { - auto ret = read(fd, &byte, 1); + auto ret = read(fd_, &byte, 1); if (ret == 1) { return *this; } @@ -114,13 +134,13 @@ linyaps_box::utils::file_descriptor::operator>>(std::byte &byte) } } -std::filesystem::path linyaps_box::utils::file_descriptor::proc_path() const +auto linyaps_box::utils::file_descriptor::proc_path() const -> std::filesystem::path { return std::filesystem::current_path().root_path() / "proc" / "self" / "fd" - / std::to_string(fd); + / std::to_string(fd_); } -std::filesystem::path linyaps_box::utils::file_descriptor::current_path() const noexcept +auto linyaps_box::utils::file_descriptor::current_path() const noexcept -> std::filesystem::path { std::error_code ec; auto p_path = proc_path(); @@ -131,3 +151,8 @@ std::filesystem::path linyaps_box::utils::file_descriptor::current_path() const return path; } + +auto linyaps_box::utils::file_descriptor::cwd() -> file_descriptor +{ + return file_descriptor{ AT_FDCWD }; +} diff --git a/src/linyaps_box/utils/file_describer.h b/src/linyaps_box/utils/file_describer.h index 0b85205..af8d336 100644 --- a/src/linyaps_box/utils/file_describer.h +++ b/src/linyaps_box/utils/file_describer.h @@ -14,6 +14,26 @@ class file_descriptor_closed_exception : public std::runtime_error { public: file_descriptor_closed_exception(); + file_descriptor_closed_exception(const file_descriptor_closed_exception &) = default; + file_descriptor_closed_exception(file_descriptor_closed_exception &&) noexcept = default; + auto operator=(const file_descriptor_closed_exception &) + -> file_descriptor_closed_exception & = default; + auto operator=(file_descriptor_closed_exception &&) noexcept + -> file_descriptor_closed_exception & = default; + ~file_descriptor_closed_exception() noexcept override; +}; + +class file_descriptor_invalid_exception : public std::runtime_error +{ +public: + explicit file_descriptor_invalid_exception(const std::string &message); + file_descriptor_invalid_exception(const file_descriptor_invalid_exception &) = default; + file_descriptor_invalid_exception(file_descriptor_invalid_exception &&) noexcept = default; + auto operator=(const file_descriptor_invalid_exception &) + -> file_descriptor_invalid_exception & = default; + auto operator=(file_descriptor_invalid_exception &&) noexcept + -> file_descriptor_invalid_exception & = default; + ~file_descriptor_invalid_exception() noexcept override; }; class file_descriptor @@ -24,28 +44,29 @@ class file_descriptor ~file_descriptor(); file_descriptor(const file_descriptor &) = delete; - file_descriptor &operator=(const file_descriptor &) = delete; + auto operator=(const file_descriptor &) -> file_descriptor & = delete; file_descriptor(file_descriptor &&other) noexcept; + auto operator=(file_descriptor &&other) noexcept -> file_descriptor &; - file_descriptor &operator=(file_descriptor &&other) noexcept; + [[nodiscard]] auto get() const noexcept -> int; - [[nodiscard]] int get() const noexcept; + auto release() -> void; - int release() && noexcept; + [[nodiscard]] auto duplicate() const -> file_descriptor; - [[nodiscard]] file_descriptor duplicate() const; + auto operator<<(const std::byte &byte) -> file_descriptor &; - file_descriptor &operator<<(const std::byte &byte); + auto operator>>(std::byte &byte) -> file_descriptor &; - file_descriptor &operator>>(std::byte &byte); + [[nodiscard]] auto proc_path() const -> std::filesystem::path; - [[nodiscard]] std::filesystem::path proc_path() const; + [[nodiscard]] auto current_path() const noexcept -> std::filesystem::path; - [[nodiscard]] std::filesystem::path current_path() const noexcept; + static auto cwd() -> file_descriptor; private: - int fd{ -1 }; + int fd_{ -1 }; }; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/fstat.cpp b/src/linyaps_box/utils/fstat.cpp index c7e7391..97e6aa5 100644 --- a/src/linyaps_box/utils/fstat.cpp +++ b/src/linyaps_box/utils/fstat.cpp @@ -3,3 +3,161 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "linyaps_box/utils/fstat.h" + +#include "linyaps_box/utils/log.h" + +#include + +namespace linyaps_box::utils { + +auto fstatat(const file_descriptor &fd, std::filesystem::path path, int flag) -> struct stat +{ + if (!path.empty() && path.is_absolute()) { + path = path.lexically_relative("/"); + } + + struct stat statbuf{}; + auto ret = ::fstatat(fd.get(), path.c_str(), &statbuf, flag); + if (ret == -1) { + throw std::system_error(errno, std::generic_category(), "fstatat"); + } + + return statbuf; + +} + +auto fstatat(const file_descriptor &fd, const std::filesystem::path &path) -> struct stat + +{ + return linyaps_box::utils::fstatat(fd, path, AT_EMPTY_PATH); + +} + +auto lstatat(const file_descriptor &fd, const std::filesystem::path &path) -> struct stat + +{ + return linyaps_box::utils::fstatat(fd, path, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + +} + +auto statfs(const file_descriptor &fd) -> struct statfs + +{ + struct statfs statbuf{}; + auto ret = ::statfs(fd.proc_path().c_str(), &statbuf); + if (ret == -1) { + throw std::system_error(errno, std::generic_category(), "statfs"); + } + + return statbuf; + +} + +auto +to_linux_file_type(std::filesystem::file_type type) noexcept -> int + +{ + switch (type) { + case std::filesystem::file_type::regular: + return S_IFREG; + case std::filesystem::file_type::directory: + return S_IFDIR; + case std::filesystem::file_type::symlink: + return S_IFLNK; + case std::filesystem::file_type::block: + return S_IFBLK; + case std::filesystem::file_type::character: + return S_IFCHR; + case std::filesystem::file_type::fifo: + return S_IFIFO; + case std::filesystem::file_type::socket: + return S_IFSOCK; + case std::filesystem::file_type::unknown: { + LINYAPS_BOX_WARNING() << "Try to convert unknown type to linux file type"; + return 0; + } + case std::filesystem::file_type::none: { + LINYAPS_BOX_DEBUG() << "Try to convert none type to linux file type"; + assert(false); + return -1; + } + case std::filesystem::file_type::not_found: { + LINYAPS_BOX_WARNING() << "Try to convert not_found type to linux file type"; + assert(false); + return -1; + } + default: { + LINYAPS_BOX_ERR() << "Try to convert unhandled file type " << static_cast(type) + << " to linux file type"; + assert(false); + return -1; + } + } +} + +auto to_fs_file_type(mode_t type) noexcept -> std::filesystem::file_type +{ + switch (type) { + case S_IFREG: + return std::filesystem::file_type::regular; + case S_IFDIR: + return std::filesystem::file_type::directory; + case S_IFLNK: + return std::filesystem::file_type::symlink; + case S_IFBLK: + return std::filesystem::file_type::block; + case S_IFCHR: + return std::filesystem::file_type::character; + case S_IFIFO: + return std::filesystem::file_type::fifo; + case S_IFSOCK: + return std::filesystem::file_type::socket; + default: + return std::filesystem::file_type::unknown; + } +} + +auto is_type(mode_t mode, std::filesystem::file_type type) noexcept -> bool +{ + auto f_type = to_linux_file_type(type); + if (f_type <= 0) { + return false; + } + + return is_type(mode, f_type); +} + +auto is_type(mode_t mode, mode_t type) noexcept -> bool +{ + return (mode & S_IFMT) == type; +} + +auto to_string(std::filesystem::file_type type) noexcept -> std::string_view +{ + switch (type) { + case std::filesystem::file_type::none: + return "None"; + case std::filesystem::file_type::not_found: + return "Not found"; + case std::filesystem::file_type::regular: + return "Regular"; + case std::filesystem::file_type::directory: + return "Directory"; + case std::filesystem::file_type::symlink: + return "Symlink"; + case std::filesystem::file_type::block: + return "Block"; + case std::filesystem::file_type::character: + return "Character"; + case std::filesystem::file_type::fifo: + return "FIFO"; + case std::filesystem::file_type::socket: + return "Socket"; + case std::filesystem::file_type::unknown: + return "Unknown"; + } + + __builtin_unreachable(); +} + +} // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/fstat.h b/src/linyaps_box/utils/fstat.h index afc9b3d..5a127a1 100644 --- a/src/linyaps_box/utils/fstat.h +++ b/src/linyaps_box/utils/fstat.h @@ -12,41 +12,14 @@ #include namespace linyaps_box::utils { - -inline struct stat fstatat(const file_descriptor &fd, std::filesystem::path path, int flag) -{ - if (!path.empty() && path.is_absolute()) { - path = path.lexically_relative("/"); - } - - struct stat statbuf{}; - auto ret = ::fstatat(fd.get(), path.c_str(), &statbuf, flag); - if (ret == -1) { - throw std::system_error(errno, std::generic_category(), "fstatat"); - } - - return statbuf; -} - -inline struct stat fstatat(const file_descriptor &fd, const std::filesystem::path &path) -{ - return linyaps_box::utils::fstatat(fd, path, AT_EMPTY_PATH); -} - -inline struct stat lstatat(const file_descriptor &fd, const std::filesystem::path &path) -{ - return linyaps_box::utils::fstatat(fd, path, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -} - -inline struct statfs statfs(const file_descriptor &fd) -{ - struct statfs statbuf{}; - auto ret = ::statfs(fd.proc_path().c_str(), &statbuf); - if (ret == -1) { - throw std::system_error(errno, std::generic_category(), "statfs"); - } - - return statbuf; -} - +auto fstatat(const file_descriptor &fd, std::filesystem::path path, int flag) -> struct stat; +auto fstatat(const file_descriptor &fd, const std::filesystem::path &path) -> struct stat; +auto lstatat(const file_descriptor &fd, const std::filesystem::path &path) -> struct stat; +auto statfs(const file_descriptor &fd) -> struct statfs; + +auto to_linux_file_type(std::filesystem::file_type type) noexcept -> int; +auto to_fs_file_type(mode_t type) noexcept -> std::filesystem::file_type; +auto is_type(mode_t mode, std::filesystem::file_type type) noexcept -> bool; +auto is_type(mode_t mode, mode_t type) noexcept -> bool; +auto to_string(std::filesystem::file_type type) noexcept -> std::string_view; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/inspect.cpp b/src/linyaps_box/utils/inspect.cpp index 3266de1..58f0ae7 100644 --- a/src/linyaps_box/utils/inspect.cpp +++ b/src/linyaps_box/utils/inspect.cpp @@ -17,7 +17,7 @@ namespace { -std::string inspect_fdinfo(const std::filesystem::path &fdinfoPath) +auto inspect_fdinfo(const std::filesystem::path &fdinfoPath) -> std::string { std::ifstream fdinfo(fdinfoPath); assert(fdinfo.is_open()); @@ -42,7 +42,7 @@ std::string inspect_fdinfo(const std::filesystem::path &fdinfoPath) return ss.str(); } -std::string inspect_fdinfo(int fd) +auto inspect_fdinfo(int fd) -> std::string { std::stringstream ss; ss << linyaps_box::utils::inspect_path(fd) << " "; @@ -54,7 +54,7 @@ std::string inspect_fdinfo(int fd) namespace linyaps_box::utils { -std::string inspect_fcntl_or_open_flags(size_t flags) +auto inspect_fcntl_or_open_flags(size_t flags) -> std::string { std::stringstream ss; @@ -126,12 +126,12 @@ std::string inspect_fcntl_or_open_flags(size_t flags) return ss.str(); } -std::string inspect_fd(int fd) +auto inspect_fd(int fd) -> std::string { return inspect_fdinfo(fd); } -std::string inspect_fds() +auto inspect_fds() -> std::string { std::stringstream ss; @@ -160,7 +160,7 @@ std::string inspect_fds() return ss.str(); } -std::string inspect_permissions(int fd) +auto inspect_permissions(int fd) -> std::string { std::stringstream ss; struct stat buf{}; @@ -228,7 +228,7 @@ std::string inspect_permissions(int fd) return ss.str(); } -std::filesystem::path inspect_path(int fd) +auto inspect_path(int fd) -> std::filesystem::path { std::error_code ec; auto ret = std::filesystem::read_symlink("/proc/self/fd/" + std::to_string(fd), ec); diff --git a/src/linyaps_box/utils/inspect.h b/src/linyaps_box/utils/inspect.h index d15633c..ff85155 100644 --- a/src/linyaps_box/utils/inspect.h +++ b/src/linyaps_box/utils/inspect.h @@ -9,10 +9,10 @@ namespace linyaps_box::utils { -std::string inspect_fcntl_or_open_flags(size_t flags); -std::string inspect_fd(int fd); -std::string inspect_fds(); -std::string inspect_permissions(int fd); -std::filesystem::path inspect_path(int fd); +auto inspect_fcntl_or_open_flags(size_t flags) -> std::string; +auto inspect_fd(int fd) -> std::string; +auto inspect_fds() -> std::string; +auto inspect_permissions(int fd) -> std::string; +auto inspect_path(int fd) -> std::filesystem::path; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/log.cpp b/src/linyaps_box/utils/log.cpp index 6365323..4bdcf98 100644 --- a/src/linyaps_box/utils/log.cpp +++ b/src/linyaps_box/utils/log.cpp @@ -68,31 +68,32 @@ template class Logger; template class Logger; template class Logger; -bool force_log_to_stderr() +auto force_log_to_stderr() -> bool { static const auto *result = getenv("LINYAPS_BOX_LOG_FORCE_STDERR"); return result != nullptr; } -bool stderr_is_a_tty() +auto stderr_is_a_tty() -> bool { static const bool result = isatty(fileno(stderr)) != 0; return result; } namespace { -unsigned int get_current_log_level_from_env() +auto get_current_log_level_from_env() -> unsigned int { auto *env = getenv("LINYAPS_BOX_LOG_LEVEL"); if (env == nullptr) { return LINYAPS_BOX_LOG_DEFAULT_LEVEL; } - auto level = std::stoi(env); - if (level < 0) { + auto ret = std::stoi(env); + if (ret < 0) { return LOG_ALERT; } + auto level = static_cast(ret); if (level > LOG_DEBUG) { return LOG_DEBUG; } @@ -101,13 +102,13 @@ unsigned int get_current_log_level_from_env() } } // namespace -unsigned int get_current_log_level() +auto get_current_log_level() -> unsigned int { static const unsigned int level = get_current_log_level_from_env(); return level; } -std::string get_pid_namespace(int pid) +auto get_pid_namespace(int pid) -> std::string { const auto &pidns_path = "/proc/" + ((pid != 0) ? std::to_string(pid) : "self") + "/ns/pid"; @@ -117,16 +118,25 @@ std::string get_pid_namespace(int pid) return "not available"; } - std::string result{ buf.begin(), buf.begin() + length }; - if (result.rfind("pid:[", 0) != 0) { - std::abort(); + const std::string_view result(buf.data(), static_cast(length)); + constexpr std::string_view prefix = "pid:["; + constexpr char suffix = ']'; + constexpr auto prefix_len = prefix.size(); + constexpr auto total_wrapper_len = prefix_len + 1; // "pid:[" + "]" + + if (result.size() < total_wrapper_len) { + return "invalid format"; + } + + if (result.rfind(prefix, 0) != 0) { + return "invalid format"; } - if (result.back() != ']') { - std::abort(); + if (result.back() != suffix) { + return "invalid format"; } - return result.substr(sizeof("pid:[") - 1, result.size() - 6); + return std::string{ result.substr(prefix_len, result.size() - total_wrapper_len) }; } } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/log.h b/src/linyaps_box/utils/log.h index e97e903..3d01a31 100644 --- a/src/linyaps_box/utils/log.h +++ b/src/linyaps_box/utils/log.h @@ -13,11 +13,11 @@ // TODO: maybe fmt with spdlog namespace linyaps_box::utils { -bool force_log_to_stderr(); -bool stderr_is_a_tty(); -unsigned int get_current_log_level(); -std::string get_pid_namespace(int pid = 0); -std::string get_current_command(); +auto force_log_to_stderr() -> bool; +auto stderr_is_a_tty() -> bool; +auto get_current_log_level() -> unsigned int; +auto get_pid_namespace(int pid = 0) -> std::string; +auto get_current_command() -> std::string; template class Logger @@ -25,7 +25,7 @@ class Logger public: Logger() = default; Logger(const Logger &) = delete; - Logger &operator=(const Logger &) = delete; + auto operator=(const Logger &) -> Logger & = delete; Logger(Logger &&) noexcept(std::is_nothrow_move_constructible_v) = // NOLINT default; Logger & @@ -34,13 +34,13 @@ class Logger ~Logger() noexcept; template - Logger &operator<<(const T &value) + auto operator<<(const T &value) -> Logger & { ss << value; return *this; } - Logger &operator<<(std::ostream &(*manipulator)(std::ostream &)) + auto operator<<(std::ostream &(*manipulator)(std::ostream &)) -> Logger & { manipulator(ss); return *this; diff --git a/src/linyaps_box/utils/mkdir.cpp b/src/linyaps_box/utils/mkdir.cpp index f5cafd8..7ee1d3a 100644 --- a/src/linyaps_box/utils/mkdir.cpp +++ b/src/linyaps_box/utils/mkdir.cpp @@ -12,9 +12,8 @@ #include #include -linyaps_box::utils::file_descriptor linyaps_box::utils::mkdir(const file_descriptor &root, - std::filesystem::path path, - mode_t mode) +auto linyaps_box::utils::mkdir(const file_descriptor &root, std::filesystem::path path, mode_t mode) + -> linyaps_box::utils::file_descriptor { LINYAPS_BOX_DEBUG() << "mkdir " << path << " at " << inspect_fd(root.get()); diff --git a/src/linyaps_box/utils/mkdir.h b/src/linyaps_box/utils/mkdir.h index f3fb863..04ebf84 100644 --- a/src/linyaps_box/utils/mkdir.h +++ b/src/linyaps_box/utils/mkdir.h @@ -6,8 +6,11 @@ #include "linyaps_box/utils/file_describer.h" +#include + namespace linyaps_box::utils { -file_descriptor mkdir(const file_descriptor &root, std::filesystem::path path, mode_t mode = 0755); +auto mkdir(const file_descriptor &root, std::filesystem::path path, mode_t mode = 0755) + -> file_descriptor; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/mknod.h b/src/linyaps_box/utils/mknod.h index 4b00e61..e0dab3b 100644 --- a/src/linyaps_box/utils/mknod.h +++ b/src/linyaps_box/utils/mknod.h @@ -8,6 +8,8 @@ #include +#include + namespace linyaps_box::utils { void mknodat(const file_descriptor &root, const std::filesystem::path &path, diff --git a/src/linyaps_box/utils/open_file.cpp b/src/linyaps_box/utils/open_file.cpp index a07ad8c..20f3e8a 100644 --- a/src/linyaps_box/utils/open_file.cpp +++ b/src/linyaps_box/utils/open_file.cpp @@ -24,15 +24,15 @@ #endif namespace { -linyaps_box::utils::file_descriptor -open_at_fallback(const linyaps_box::utils::file_descriptor &root, - const std::filesystem::path &path, - int flag, - int mode) +auto open_at_fallback(const linyaps_box::utils::file_descriptor &root, + const std::filesystem::path &path, + int flag, + mode_t mode) -> linyaps_box::utils::file_descriptor { LINYAPS_BOX_DEBUG() << "fallback openat " << path.c_str() << " at FD=" << root.get() << " with " - << linyaps_box::utils::inspect_fcntl_or_open_flags(flag) << "\n\t" - << linyaps_box::utils::inspect_fd(root.get()); + << linyaps_box::utils::inspect_fcntl_or_open_flags( + static_cast(flag)) + << "\n\t" << linyaps_box::utils::inspect_fd(root.get()); // TODO: we need implement a compatible fallback // currently we just use openat and do some simple check const auto &file_path = path.relative_path(); @@ -47,8 +47,8 @@ open_at_fallback(const linyaps_box::utils::file_descriptor &root, return linyaps_box::utils::file_descriptor{ fd }; } -linyaps_box::utils::file_descriptor -syscall_openat2(int dirfd, const char *path, uint64_t flag, uint64_t mode, uint64_t resolve) +auto syscall_openat2(int dirfd, const char *path, uint64_t flag, uint64_t mode, uint64_t resolve) + -> linyaps_box::utils::file_descriptor { struct openat2_how { @@ -66,11 +66,11 @@ syscall_openat2(int dirfd, const char *path, uint64_t flag, uint64_t mode, uint6 } } // namespace -linyaps_box::utils::file_descriptor linyaps_box::utils::open(const std::filesystem::path &path, - int flag, - mode_t mode) +auto linyaps_box::utils::open(const std::filesystem::path &path, int flag, mode_t mode) + -> linyaps_box::utils::file_descriptor { - LINYAPS_BOX_DEBUG() << "open " << path.c_str() << " with " << inspect_fcntl_or_open_flags(flag); + LINYAPS_BOX_DEBUG() << "open " << path.c_str() << " with " + << inspect_fcntl_or_open_flags(static_cast(flag)); const auto fd = ::open(path.c_str(), flag, mode); if (fd == -1) { throw std::system_error(errno, @@ -81,19 +81,23 @@ linyaps_box::utils::file_descriptor linyaps_box::utils::open(const std::filesyst return linyaps_box::utils::file_descriptor{ fd }; } -linyaps_box::utils::file_descriptor -linyaps_box::utils::open_at(const linyaps_box::utils::file_descriptor &root, - const std::filesystem::path &path, - int flag, - mode_t mode) +auto linyaps_box::utils::open_at(const linyaps_box::utils::file_descriptor &root, + const std::filesystem::path &path, + int flag, + mode_t mode) -> linyaps_box::utils::file_descriptor { LINYAPS_BOX_DEBUG() << "open " << path.c_str() << " at FD=" << root.get() << " with " - << inspect_fcntl_or_open_flags(flag) << "\n\t" << inspect_fd(root.get()); + << inspect_fcntl_or_open_flags(static_cast(flag)) << "\n\t" + << inspect_fd(root.get()); static bool support_openat2{ true }; while (support_openat2) { try { - return syscall_openat2(root.get(), path.c_str(), flag, mode, RESOLVE_IN_ROOT); + return syscall_openat2(root.get(), + path.c_str(), + static_cast(flag), + mode, + RESOLVE_IN_ROOT); } catch (const std::system_error &e) { const auto code = e.code().value(); if (code == EINTR || code == EAGAIN) { diff --git a/src/linyaps_box/utils/open_file.h b/src/linyaps_box/utils/open_file.h index 54b4d0b..7063753 100644 --- a/src/linyaps_box/utils/open_file.h +++ b/src/linyaps_box/utils/open_file.h @@ -12,11 +12,12 @@ namespace linyaps_box::utils { -file_descriptor open(const std::filesystem::path &path, int flag = O_RDONLY, mode_t mode = 0); +auto open(const std::filesystem::path &path, int flag = O_PATH | O_CLOEXEC, mode_t mode = 0) + -> file_descriptor; -file_descriptor open_at(const file_descriptor &root, - const std::filesystem::path &path, - int flag = O_RDONLY, - mode_t mode = 0); +auto open_at(const file_descriptor &root, + const std::filesystem::path &path, + int flag = O_PATH | O_CLOEXEC, + mode_t mode = 0) -> file_descriptor; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/platform.cpp b/src/linyaps_box/utils/platform.cpp index 6ea345b..18ab929 100644 --- a/src/linyaps_box/utils/platform.cpp +++ b/src/linyaps_box/utils/platform.cpp @@ -11,11 +11,11 @@ #include namespace linyaps_box::utils { -int str_to_signal(std::string_view str) +auto str_to_signal(std::string_view str) -> int { // Only support standard signals for now, // TODO: support real-time signal in the future - static const std::unordered_map sigMap{ + const std::unordered_map sigMap{ { "SIGABRT", SIGABRT }, { "SIGALRM", SIGALRM }, { "SIGBUS", SIGBUS }, { "SIGCHLD", SIGCHLD }, { "SIGCONT", SIGCONT }, { "SIGFPE", SIGFPE }, { "SIGHUP", SIGHUP }, { "SIGILL", SIGILL }, { "SIGINT", SIGINT }, @@ -26,7 +26,10 @@ int str_to_signal(std::string_view str) { "SIGTTIN", SIGTTIN }, { "SIGTTOU", SIGTTOU }, { "SIGURG", SIGURG }, { "SIGUSR1", SIGUSR1 }, { "SIGUSR2", SIGUSR2 }, { "SIGVTALRM", SIGVTALRM }, { "SIGWINCH", SIGWINCH }, { "SIGXCPU", SIGXCPU }, { "SIGXFSZ", SIGXFSZ }, - { "SIGIO", SIGIO }, { "SIGIOT", SIGIOT }, { "SIGCLD", SIGCLD }, + { "SIGIO", SIGIO }, { "SIGIOT", SIGIOT }, +#ifdef SIGCLD + { "SIGCLD", SIGCLD }, +#endif }; auto it = sigMap.find(str); @@ -37,9 +40,9 @@ int str_to_signal(std::string_view str) return it->second; } -int str_to_rlimit(std::string_view str) +auto str_to_rlimit(std::string_view str) -> int { - const static std::unordered_map resources{ + const std::unordered_map resources{ { "RLIMIT_AS", RLIMIT_AS }, { "RLIMIT_CORE", RLIMIT_CORE }, { "RLIMIT_CPU", RLIMIT_CPU }, diff --git a/src/linyaps_box/utils/platform.h b/src/linyaps_box/utils/platform.h index c72b96f..7cc2f8a 100644 --- a/src/linyaps_box/utils/platform.h +++ b/src/linyaps_box/utils/platform.h @@ -6,6 +6,6 @@ #include namespace linyaps_box::utils { -int str_to_signal(std::string_view str); -int str_to_rlimit(std::string_view str); +auto str_to_signal(std::string_view str) -> int; +auto str_to_rlimit(std::string_view str) -> int; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/semver.cpp b/src/linyaps_box/utils/semver.cpp index 37a81c1..2e340b7 100644 --- a/src/linyaps_box/utils/semver.cpp +++ b/src/linyaps_box/utils/semver.cpp @@ -16,7 +16,11 @@ linyaps_box::utils::semver::semver(const std::string &str) throw std::invalid_argument("invalid semver: " + str); } - auto major = std::stoi(str.substr(begin, end)); + auto ret = std::stoi(str.substr(begin, end)); + if (ret < 0) { + throw std::invalid_argument("invalid semver: " + str); + } + const auto major{ static_cast(ret) }; begin = end + 1; end = str.find('.', begin); @@ -24,12 +28,20 @@ linyaps_box::utils::semver::semver(const std::string &str) throw std::invalid_argument("invalid semver: " + str); } - auto minor = std::stoi(str.substr(begin, end)); + ret = std::stoi(str.substr(begin, end)); + if (ret < 0) { + throw std::invalid_argument("invalid semver: " + str); + } + const auto minor{ static_cast(ret) }; begin = end + 1; end = str.find_first_of("-+", begin); - auto patch = std::stoi(str.substr(begin, end)); + ret = std::stoi(str.substr(begin, end)); + if (ret < 0) { + throw std::invalid_argument("invalid semver: " + str); + } + const auto patch{ static_cast(ret) }; if (end == std::string::npos) { this->major_ = major; diff --git a/src/linyaps_box/utils/semver.h b/src/linyaps_box/utils/semver.h index 118e8b7..e63c021 100644 --- a/src/linyaps_box/utils/semver.h +++ b/src/linyaps_box/utils/semver.h @@ -19,14 +19,14 @@ class semver explicit semver(const std::string &str); - [[nodiscard]] unsigned int major() const; - [[nodiscard]] unsigned int minor() const; - [[nodiscard]] unsigned int patch() const; - [[nodiscard]] const std::string &prerelease() const; - [[nodiscard]] const std::string &build() const; - - [[nodiscard]] std::string to_string() const; - [[nodiscard]] bool is_compatible_with(const semver &other) const; + [[nodiscard]] auto major() const -> unsigned int; + [[nodiscard]] auto minor() const -> unsigned int; + [[nodiscard]] auto patch() const -> unsigned int; + [[nodiscard]] auto prerelease() const -> const std::string &; + [[nodiscard]] auto build() const -> const std::string &; + + [[nodiscard]] auto to_string() const -> std::string; + [[nodiscard]] auto is_compatible_with(const semver &other) const -> bool; private: unsigned int major_; diff --git a/src/linyaps_box/utils/socketpair.cpp b/src/linyaps_box/utils/socketpair.cpp index 99ef544..85dd311 100644 --- a/src/linyaps_box/utils/socketpair.cpp +++ b/src/linyaps_box/utils/socketpair.cpp @@ -3,3 +3,23 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "linyaps_box/utils/socketpair.h" + +#include +#include + +namespace linyaps_box::utils { + +auto socketpair(int domain, int type, int protocol) -> std::pair +{ + std::array fds{}; + if (::socketpair(domain, type, protocol, fds.data()) == -1) { + throw std::system_error(errno, + std::system_category(), + "socketpair(" + std::to_string(domain) + ", " + std::to_string(type) + + ", " + std::to_string(protocol) + ")"); + } + + return std::make_pair(file_descriptor(fds[0]), file_descriptor(fds[1])); +} + +} // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/socketpair.h b/src/linyaps_box/utils/socketpair.h index be12179..0dba48c 100644 --- a/src/linyaps_box/utils/socketpair.h +++ b/src/linyaps_box/utils/socketpair.h @@ -4,25 +4,11 @@ #include "linyaps_box/utils/file_describer.h" -#include -#include - #include #include namespace linyaps_box::utils { -inline std::pair socketpair(int domain, int type, int protocol) -{ - std::array fds{}; - if (::socketpair(domain, type, protocol, fds.data()) == -1) { - throw std::system_error(errno, - std::system_category(), - "socketpair(" + std::to_string(domain) + ", " + std::to_string(type) - + ", " + std::to_string(protocol) + ")"); - } - - return std::make_pair(file_descriptor(fds[0]), file_descriptor(fds[1])); -} +auto socketpair(int domain, int type, int protocol) -> std::pair; } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/symlink.cpp b/src/linyaps_box/utils/symlink.cpp index ab6f64f..e22eee0 100644 --- a/src/linyaps_box/utils/symlink.cpp +++ b/src/linyaps_box/utils/symlink.cpp @@ -6,6 +6,10 @@ #include "linyaps_box/utils/log.h" +#include + +#include + void linyaps_box::utils::symlink(const std::filesystem::path &target, const std::filesystem::path &link_path) { @@ -30,3 +34,26 @@ void linyaps_box::utils::symlink_at(const std::filesystem::path &target, throw std::system_error(errno, std::system_category(), "symlinkat"); } } + +std::filesystem::path linyaps_box::utils::readlink(const std::filesystem::path &path) +{ + std::error_code ec; + auto ret = std::filesystem::read_symlink(path, ec); + if (ec) { + throw std::system_error{ ec.value(), std::system_category(), ec.message() }; + } + + return ret; +} + +std::filesystem::path linyaps_box::utils::readlinkat(const file_descriptor &dirfd, + const std::filesystem::path &path) +{ + std::array buf{}; + auto ret = ::readlinkat(dirfd.get(), path.c_str(), buf.data(), PATH_MAX + 1); + if (ret == -1) { + throw std::system_error(errno, std::system_category(), "readlinkat"); + } + + return std::filesystem::path{ buf.data() }; +} diff --git a/src/linyaps_box/utils/symlink.h b/src/linyaps_box/utils/symlink.h index dcbab84..434300a 100644 --- a/src/linyaps_box/utils/symlink.h +++ b/src/linyaps_box/utils/symlink.h @@ -16,4 +16,8 @@ void symlink_at(const std::filesystem::path &target, const file_descriptor &dirfd, const std::filesystem::path &link_path); +std::filesystem::path readlink(const std::filesystem::path &path); + +std::filesystem::path readlinkat(const file_descriptor &dirfd, const std::filesystem::path &path); + } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/touch.cpp b/src/linyaps_box/utils/touch.cpp index bb215f0..becc89e 100644 --- a/src/linyaps_box/utils/touch.cpp +++ b/src/linyaps_box/utils/touch.cpp @@ -9,11 +9,13 @@ #include -linyaps_box::utils::file_descriptor linyaps_box::utils::touch(const file_descriptor &root, - const std::filesystem::path &path) +auto linyaps_box::utils::touch(const file_descriptor &root, + const std::filesystem::path &path, + int flag, + mode_t mode) -> linyaps_box::utils::file_descriptor { LINYAPS_BOX_DEBUG() << "touch " << path << " at " << inspect_fd(root.get()); - const auto fd = ::openat(root.get(), path.c_str(), O_CREAT | O_WRONLY, 0666); + const auto fd = ::openat(root.get(), path.c_str(), flag, mode); if (fd == -1) { throw std::system_error(errno, std::system_category(), diff --git a/src/linyaps_box/utils/touch.h b/src/linyaps_box/utils/touch.h index 5bd8da1..f1ffb0f 100644 --- a/src/linyaps_box/utils/touch.h +++ b/src/linyaps_box/utils/touch.h @@ -6,8 +6,13 @@ #include "linyaps_box/utils/file_describer.h" +#include + namespace linyaps_box::utils { -file_descriptor touch(const file_descriptor &root, const std::filesystem::path &path); +auto touch(const file_descriptor &root, + const std::filesystem::path &path, + int flag, + mode_t mode = 0644) -> file_descriptor; } // namespace linyaps_box::utils diff --git a/tests/ll-box-st/12-bind-host-dev.json b/tests/ll-box-st/12-bind-host-dev.json index d9662c7..f0463b8 100644 --- a/tests/ll-box-st/12-bind-host-dev.json +++ b/tests/ll-box-st/12-bind-host-dev.json @@ -5,7 +5,7 @@ "uid": 0, "gid": 0 }, - "args": ["/bin/env", "bash", "-c", "pwd"], + "args": ["true"], "env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm", diff --git a/tests/ll-box-st/13-pid-extension.json b/tests/ll-box-st/13-pid-extension.json new file mode 100644 index 0000000..f0ea486 --- /dev/null +++ b/tests/ll-box-st/13-pid-extension.json @@ -0,0 +1,29 @@ +{ + "name": "Set ns_last_pid", + "process": { + "user": { + "uid": 0, + "gid": 0 + }, + "args": ["/bin/env", "bash", "-c", "cat /proc/sys/kernel/ns_last_pid"], + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "HOME=/root" + ], + "cwd": "/" + }, + "annotations": { + "cn.org.linyaps.runtime.ns_last_pid": "100" + }, + "linux": { + "readonlyPaths": [ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sysrq-trigger" + ] + }, + "expected": "100" +} diff --git a/tests/ll-box-st/ll-box-st b/tests/ll-box-st/ll-box-st index cc1b944..6f2a8cd 100755 --- a/tests/ll-box-st/ll-box-st +++ b/tests/ll-box-st/ll-box-st @@ -175,17 +175,22 @@ function run_test() { mkdir -p -- "${TEST_DIR}" local TEST_CONFIG="${TEST_DIR}/test_config.json" - # patch process from test file - "${JQ}" >"${TEST_CONFIG}" \ - ".process |= $("${JQ}" -r '.process' "${TEST_FILE}" || true)" \ - "${BUNDLE_DIR}/config.json" - - # patch mount from test file if test file has mount field - if "${JQ}" -e '.mounts' "${TEST_FILE}" &>/dev/null; then - "${JQ}" >"${TEST_CONFIG}" \ - ".mounts |= $("${JQ}" -r '.mounts' "${TEST_FILE}" || true)" \ - "${BUNDLE_DIR}/config.json" - fi + + # Create a copy of the original config and apply all patches at once + "${JQ}" -s ' + .[0] as $base | + .[1] as $patch | + $base * { + process: ($patch.process // $base.process), + mounts: ($patch.mounts // $base.mounts), + annotations: ($patch.annotations // $base.annotations), + linux: (if $patch.linux then + $base.linux * $patch.linux + else + $base.linux + end) + } + ' "${BUNDLE_DIR}/config.json" "${TEST_FILE}" > "${TEST_CONFIG}" local CONTAINER_NAME CONTAINER_NAME="$(basename -- "${TEST_FILE_NAME}" .json)" diff --git a/tests/ll-box-ut/src/test.cpp b/tests/ll-box-ut/src/test.cpp index db02749..e3bf8b4 100644 --- a/tests/ll-box-ut/src/test.cpp +++ b/tests/ll-box-ut/src/test.cpp @@ -22,6 +22,7 @@ #pragma GCC diagnostic pop #endif -TEST(LINYAPS_BOX, Placeholder1) { - EXPECT_EQ(1, 1); +TEST(LINYAPS_BOX, Placeholder1) +{ + EXPECT_EQ(1, 1); } diff --git a/tools/format.sh b/tools/format.sh new file mode 100755 index 0000000..8f1147c --- /dev/null +++ b/tools/format.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +# tools/format.sh +# Format all C/C++ source files in the project using clang-format. +# +# Usage: +# ./tools/format.sh # Use system default clang-format +# ./tools/format.sh clang-format-17 # Use a specific clang-format binary +# +# The script automatically skips common build and third-party directories. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +CLANG_FORMAT="${1:-clang-format}" + +# Check clang-format availability +if ! command -v "${CLANG_FORMAT}" >/dev/null 2>&1; then + echo "Error: ${CLANG_FORMAT} not found. Please install clang-format." >&2 + exit 1 +fi + +# --- Project root validation --- +# Criteria for identifying a valid project root: +# 1. Has CMakeLists.txt, or +# 2. Has .clang-format, or +# 3. Contains app/, src/, or tests/ directories + +VALID_ROOT=false +if [[ -f "${ROOT_DIR}/CMakeLists.txt" ]]; then + VALID_ROOT=true +elif [[ -f "${ROOT_DIR}/.clang-format" ]]; then + VALID_ROOT=true +else + for d in app src tests; do + if [ -d "${ROOT_DIR}/${d}" ]; then + VALID_ROOT=true + break + fi + done +fi + +if [[ ${VALID_ROOT} != true ]]; then + echo "Error: This script must be run inside a valid C++ project root." + echo "Expected to find one of the following in ${ROOT_DIR}:" + echo " - CMakeLists.txt" + echo " - .clang-format or _clang-format" + echo " - app/, src/, or tests/ directories" + exit 1 +fi +# --- End project root validation --- + +CLANG_FORMAT_VERSION=$("${CLANG_FORMAT}" --version | head -n 1 | cut -d ' ' -f 3) +echo "Using clang-format: ${CLANG_FORMAT_VERSION}" +echo "Project root: ${ROOT_DIR}" + +# Only search for .cpp and .h files under app, src, and tests directories +dirs=() +for d in app src tests; do + if [[ -d "${ROOT_DIR}/${d}" ]]; then + dirs+=("${ROOT_DIR}/${d}") + fi +done + +if [[ ${#dirs[@]} -eq 0 ]]; then + echo "Warning: No app/, src/, or tests/ directories found under ${ROOT_DIR}." + exit 0 +fi + +find "${dirs[@]}" \ + \( -name "*.cpp" -o -name "*.h" \) \ + -type f -print | + while read -r file; do + echo "Formatting: ${file}" + "${CLANG_FORMAT}" -i "${file}" + done + +echo "Formatting completed."