Compare commits
35 Commits
2.0.3-rc.1
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
5a4eaff29f | |
|
|
e12e421143 | |
|
|
caf2cec0d2 | |
|
|
59ed24f214 | |
|
|
27c0ae9643 | |
|
|
8f1b042421 | |
|
|
613cc27af6 | |
|
|
56bb0d1e6b | |
|
|
72ca241a20 | |
|
|
11face2e94 | |
|
|
04aface617 | |
|
|
08b790480d | |
|
|
53f0e1e835 | |
|
|
881bcc93c0 | |
|
|
0e5def0fe2 | |
|
|
9204ffba0c | |
|
|
3b94fbcf43 | |
|
|
4ad30a7efb | |
|
|
24a6a99f98 | |
|
|
b1c7fa13d8 | |
|
|
08c47fff3c | |
|
|
e62d7d53e4 | |
|
|
2b51656a6e | |
|
|
2a8c21b681 | |
|
|
61d1b26eae | |
|
|
53883d5bf4 | |
|
|
55a147033b | |
|
|
308b80bacf | |
|
|
16f40a416a | |
|
|
3951cfc9b2 | |
|
|
0bb8d07bb2 | |
|
|
ee4e07a827 | |
|
|
e31f62f382 | |
|
|
000fa7ff73 | |
|
|
1fd78ccf1a |
|
|
@ -0,0 +1,76 @@
|
|||
# SPDX-FileCopyrightText: None
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
version = 1
|
||||
|
||||
# 全局排除配置
|
||||
exclude_patterns = [
|
||||
"external/**",
|
||||
"build/**",
|
||||
"obj-*/**",
|
||||
".cache/**",
|
||||
".obs/**",
|
||||
".tx/**",
|
||||
"**/*.generated.*",
|
||||
"**/CMakeCache.txt",
|
||||
"**/compile_commands.json",
|
||||
"debian/patches/**",
|
||||
"po/*.po",
|
||||
"po/*.pot",
|
||||
"tools/openapi-c-libcurl-client/**",
|
||||
]
|
||||
|
||||
# C++ 分析器配置
|
||||
[[analyzers]]
|
||||
name = "cxx"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
misra_compliance = true
|
||||
cyclomatic_complexity_threshold = "medium"
|
||||
|
||||
# Shell 脚本分析器配置
|
||||
[[analyzers]]
|
||||
name = "shell"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
# 只保留官方支持的选项
|
||||
dialect = "bash"
|
||||
|
||||
# 密钥检测分析器配置
|
||||
[[analyzers]]
|
||||
name = "secrets"
|
||||
enabled = true
|
||||
# secrets 分析器不支持任何 meta 配置
|
||||
|
||||
# 测试覆盖率分析器配置
|
||||
[[analyzers]]
|
||||
name = "test-coverage"
|
||||
enabled = true
|
||||
|
||||
# 代码格式化器配置
|
||||
[[transformers]]
|
||||
name = "clang-format"
|
||||
enabled = true
|
||||
|
||||
[transformers.meta]
|
||||
exclude_patterns = ["external/**", "build/**", "obj-*/**", "**/*.generated.*"]
|
||||
|
||||
[[transformers]]
|
||||
name = "prettier"
|
||||
enabled = true
|
||||
|
||||
[transformers.meta]
|
||||
include_patterns = ["**/*.md", "**/*.json", "**/*.yaml", "**/*.yml"]
|
||||
exclude_patterns = [
|
||||
"external/**",
|
||||
"build/**",
|
||||
"obj-*/**",
|
||||
".cache/**",
|
||||
"po/**",
|
||||
"**/*.generated.*",
|
||||
"package-lock.json",
|
||||
"yarn.lock",
|
||||
]
|
||||
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.11.4) # for RHEL 8
|
|||
|
||||
project(
|
||||
linyaps-box
|
||||
VERSION 2.0.3
|
||||
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,18 +255,18 @@ 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()
|
||||
|
||||
list(APPEND linyaps-box_LIBRARY_LINK_LIBRARIES PUBLIC CLI11::CLI11)
|
||||
|
||||
add_library("${linyaps-box_LIBRARY}" ${linyaps-box_LIBRARY_SOURCE})
|
||||
add_library("${linyaps-box_LIBRARY}" STATIC ${linyaps-box_LIBRARY_SOURCE})
|
||||
target_include_directories("${linyaps-box_LIBRARY}"
|
||||
${linyaps-box_LIBRARY_INCLUDE_DIRS})
|
||||
target_link_libraries("${linyaps-box_LIBRARY}"
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -33,7 +34,8 @@
|
|||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_COLOR_DIAGNOSTICS": true,
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"linyaps-box_ENABLE_CPACK": "ON"
|
||||
"linyaps-box_ENABLE_CPACK": "ON",
|
||||
"linyaps-box_MAKE_RELEASE": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -46,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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
\[ **en** | [zh_CN](./README.zh_CN.md) \]
|
||||
|
||||
[](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.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
\[ [en](./README.md) | **zh_CN** \]
|
||||
|
||||
[](https://repology.org/project/linyaps-box/versions)
|
||||
|
||||
这个项目是一个简单的[OCI运行时],主要由[玲珑]使用,
|
||||
玲珑是一个用于Linux桌面应用程序分发的工具包。
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@
|
|||
#include "linyaps_box/command/list.h"
|
||||
#include "linyaps_box/command/run.h"
|
||||
#include "linyaps_box/utils/log.h"
|
||||
|
||||
#include <iostream>
|
||||
#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 (char *c = argv[i]; *c; ++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,15 +71,15 @@ 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) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
LINYAPS_BOX_ERR() << "Error: " << e.what();
|
||||
return -1;
|
||||
} catch (...) {
|
||||
std::cerr << "Error: unknown" << std::endl;
|
||||
LINYAPS_BOX_ERR() << "unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<std::string, std::string> 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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <linyaps_box/cgroup_manager.h>
|
||||
|
||||
namespace linyaps_box {
|
||||
|
||||
cgroup_manager::~cgroup_manager() = default;
|
||||
|
||||
} // namespace linyaps_box
|
||||
|
|
@ -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 @@ public:
|
|||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
void linyaps_box::command::exec(const struct exec_options &options)
|
||||
{
|
||||
std::unique_ptr<status_directory> dir =
|
||||
std::make_unique<impl::status_directory>(options.global.get().root);
|
||||
std::make_unique<impl::status_directory>(options.global_.get().root);
|
||||
runtime_t runtime(std::move(dir));
|
||||
|
||||
auto container_refs = runtime.containers();
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ void linyaps_box::command::kill(const struct kill_options &options)
|
|||
break;
|
||||
}
|
||||
|
||||
auto status_dir = std::make_unique<impl::status_directory>(options.global.get().root);
|
||||
auto status_dir = std::make_unique<impl::status_directory>(options.global_.get().root);
|
||||
if (!status_dir) {
|
||||
throw std::runtime_error("failed to create status directory");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
void linyaps_box::command::list(const struct list_options &options)
|
||||
{
|
||||
auto status_dir = std::make_unique<impl::status_directory>(options.global.get().root);
|
||||
auto status_dir = std::make_unique<impl::status_directory>(options.global_.get().root);
|
||||
if (!status_dir) {
|
||||
throw std::runtime_error("failed to create status directory");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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_options> global;
|
||||
std::reference_wrapper<global_options> 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_options> global;
|
||||
std::reference_wrapper<global_options> global_;
|
||||
std::vector<std::string> command;
|
||||
std::string user;
|
||||
std::optional<std::vector<std::string>> 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_options> global;
|
||||
std::reference_wrapper<global_options> 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_options> global;
|
||||
std::reference_wrapper<global_options> global_;
|
||||
std::string container;
|
||||
std::string signal;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<status_directory> dir =
|
||||
std::make_unique<impl::status_directory>(options.global.get().root);
|
||||
std::make_unique<impl::status_directory>(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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -11,17 +11,19 @@
|
|||
|
||||
namespace {
|
||||
|
||||
std::tuple<unsigned long, unsigned long, std::uint8_t, std::string>
|
||||
parse_mount_options(const std::vector<std::string> &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<std::string> &options)
|
||||
-> std::tuple<unsigned long, unsigned long, std::uint8_t, std::string>
|
||||
{
|
||||
const static std::unordered_map<std::string_view, unsigned long> propagation_flags_map{
|
||||
const std::unordered_map<std::string_view, unsigned long> 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<std::string_view, unsigned long> flags_map{
|
||||
const std::unordered_map<std::string_view, unsigned long> flags_map{
|
||||
{ "bind", MS_BIND },
|
||||
{ "defaults", 0 },
|
||||
{ "dirsync", MS_DIRSYNC },
|
||||
|
|
@ -43,7 +45,7 @@ parse_mount_options(const std::vector<std::string> &options)
|
|||
{ "sync", MS_SYNCHRONOUS },
|
||||
};
|
||||
|
||||
const static std::unordered_map<std::string_view, unsigned long> unset_flags_map{
|
||||
const std::unordered_map<std::string_view, unsigned long> unset_flags_map{
|
||||
{ "async", MS_SYNCHRONOUS },
|
||||
{ "atime", MS_NOATIME },
|
||||
{ "dev", MS_NODEV },
|
||||
|
|
@ -60,7 +62,7 @@ parse_mount_options(const std::vector<std::string> &options)
|
|||
{ "symfollow", LINGYAPS_MS_NOSYMFOLLOW },
|
||||
};
|
||||
|
||||
const static std::unordered_map<std::string_view, std::uint8_t> extra_flags_map{
|
||||
const std::unordered_map<std::string_view, std::uint8_t> extra_flags_map{
|
||||
{ "copy-symlink", linyaps_box::config::mount_t::COPY_SYMLINK }
|
||||
};
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ parse_mount_options(const std::vector<std::string> &options)
|
|||
continue;
|
||||
}
|
||||
if (auto it = propagation_flags_map.find(opt); it != propagation_flags_map.end()) {
|
||||
propagation_flags &= it->second;
|
||||
propagation_flags |= it->second;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -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)) {
|
||||
|
|
@ -262,12 +264,27 @@ linyaps_box::config::linux_t parse_linux(const nlohmann::json &obj,
|
|||
linux.readonly_paths = std::move(readonly_paths);
|
||||
}
|
||||
|
||||
if (auto rootfs_propagation = ptr / "rootfsPropagation"; obj.contains(rootfs_propagation)) {
|
||||
auto val = obj[rootfs_propagation].get<std::string>();
|
||||
if (val == "shared") {
|
||||
linux.rootfs_propagation = MS_SHARED;
|
||||
} else if (val == "slave") {
|
||||
linux.rootfs_propagation = MS_SLAVE;
|
||||
} else if (val == "private") {
|
||||
linux.rootfs_propagation = MS_PRIVATE;
|
||||
} else if (val == "unbindable") {
|
||||
linux.rootfs_propagation = MS_UNBINDABLE;
|
||||
} else {
|
||||
throw std::runtime_error("unsupported rootfs propagation: " + val);
|
||||
}
|
||||
}
|
||||
|
||||
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<std::string>());
|
||||
if (!linyaps_box::utils::semver(linyaps_box::config::oci_version).is_compatible_with(semver)) {
|
||||
|
|
@ -360,7 +377,7 @@ linyaps_box::config parse_1_2_0(const nlohmann::json &j)
|
|||
}
|
||||
|
||||
if (h.contains("env")) {
|
||||
std::map<std::string, std::string> env;
|
||||
std::unordered_map<std::string, std::string> env;
|
||||
|
||||
for (const auto &e : h["env"].get<std::vector<std::string>>()) {
|
||||
auto pos = e.find('=');
|
||||
|
|
@ -433,6 +450,11 @@ linyaps_box::config parse_1_2_0(const nlohmann::json &j)
|
|||
cfg.root.readonly = j[root / "readonly"].get<bool>();
|
||||
}
|
||||
|
||||
auto annotations = ptr / "annotations";
|
||||
if (j.contains(annotations)) {
|
||||
cfg.annotations = j[annotations].get<std::unordered_map<std::string, std::string>>();
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
#include <sys/mount.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/resource.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
|
||||
{
|
||||
|
|
@ -123,6 +123,7 @@ struct config
|
|||
std::optional<std::vector<id_mapping_t>> gid_mappings;
|
||||
std::optional<std::vector<std::filesystem::path>> masked_paths;
|
||||
std::optional<std::vector<std::filesystem::path>> readonly_paths;
|
||||
unsigned int rootfs_propagation{ 0 };
|
||||
};
|
||||
|
||||
std::optional<linux_t> linux;
|
||||
|
|
@ -133,7 +134,7 @@ struct config
|
|||
{
|
||||
std::filesystem::path path;
|
||||
std::optional<std::vector<std::string>> args;
|
||||
std::optional<std::map<std::string, std::string>> env;
|
||||
std::optional<std::unordered_map<std::string, std::string>> env;
|
||||
std::optional<int> timeout;
|
||||
};
|
||||
|
||||
|
|
@ -169,6 +170,8 @@ struct config
|
|||
};
|
||||
|
||||
root_t root;
|
||||
|
||||
std::optional<std::unordered_map<std::string, std::string>> annotations;
|
||||
};
|
||||
|
||||
} // namespace linyaps_box
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
|
@ -47,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 {
|
||||
|
||||
|
|
@ -54,6 +56,7 @@ struct container_data
|
|||
{
|
||||
bool deny_setgroups{ false };
|
||||
bool mount_dev_from_host{ false };
|
||||
unsigned int rootfs_propagation{ 0 };
|
||||
};
|
||||
|
||||
container_data &get_private_data(const linyaps_box::container &c) noexcept
|
||||
|
|
@ -110,96 +113,59 @@ std::ostream &operator<<(std::ostream &os, const sync_message message)
|
|||
} break;
|
||||
default: {
|
||||
assert(false);
|
||||
os << "UNKNOWN " << (uint8_t)message;
|
||||
os << "UNKNOWN " << static_cast<uint8_t>(message);
|
||||
} break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string dump_mount_flags(uint flags) noexcept
|
||||
struct MountFlag
|
||||
{
|
||||
unsigned int flag;
|
||||
std::string_view name;
|
||||
};
|
||||
|
||||
constexpr std::array<MountFlag, 27> 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<unsigned int>(MS_NOUSER), "MS_NOUSER" }
|
||||
};
|
||||
|
||||
[[maybe_unused]] auto dump_mount_flags(unsigned long flags) noexcept -> std::string
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[ ";
|
||||
if ((flags & MS_RDONLY) != 0) {
|
||||
ss << "MS_RDONLY ";
|
||||
}
|
||||
if ((flags & MS_NOSUID) != 0) {
|
||||
ss << "MS_NOSUID ";
|
||||
}
|
||||
if ((flags & MS_NODEV) != 0) {
|
||||
ss << "MS_NODEV ";
|
||||
}
|
||||
if ((flags & MS_NOEXEC) != 0) {
|
||||
ss << "MS_NOEXEC ";
|
||||
}
|
||||
if ((flags & MS_SYNCHRONOUS) != 0) {
|
||||
ss << "MS_SYNCHRONOUS ";
|
||||
}
|
||||
if ((flags & MS_REMOUNT) != 0) {
|
||||
ss << "MS_REMOUNT ";
|
||||
}
|
||||
if ((flags & MS_MANDLOCK) != 0) {
|
||||
ss << "MS_MANDLOCK ";
|
||||
}
|
||||
if ((flags & MS_DIRSYNC) != 0) {
|
||||
ss << "MS_DIRSYNC ";
|
||||
}
|
||||
if ((flags & LINGYAPS_MS_NOSYMFOLLOW) != 0) {
|
||||
ss << "MS_NOSYMFOLLOW ";
|
||||
}
|
||||
if ((flags & MS_NOATIME) != 0) {
|
||||
ss << "MS_NOATIME ";
|
||||
}
|
||||
if ((flags & MS_NODIRATIME) != 0) {
|
||||
ss << "MS_NODIRATIME ";
|
||||
}
|
||||
if ((flags & MS_BIND) != 0) {
|
||||
ss << "MS_BIND ";
|
||||
}
|
||||
if ((flags & MS_MOVE) != 0) {
|
||||
ss << "MS_MOVE ";
|
||||
}
|
||||
if ((flags & MS_REC) != 0) {
|
||||
ss << "MS_REC ";
|
||||
}
|
||||
if ((flags & MS_SILENT) != 0) {
|
||||
ss << "MS_SILENT ";
|
||||
}
|
||||
if ((flags & MS_POSIXACL) != 0) {
|
||||
ss << "MS_POSIXACL ";
|
||||
}
|
||||
if ((flags & MS_UNBINDABLE) != 0) {
|
||||
ss << "MS_UNBINDABLE ";
|
||||
}
|
||||
if ((flags & MS_PRIVATE) != 0) {
|
||||
ss << "MS_PRIVATE ";
|
||||
}
|
||||
if ((flags & MS_SLAVE) != 0) {
|
||||
ss << "MS_SLAVE ";
|
||||
}
|
||||
if ((flags & MS_SHARED) != 0) {
|
||||
ss << "MS_SHARED ";
|
||||
}
|
||||
if ((flags & MS_RELATIME) != 0) {
|
||||
ss << "MS_RELATIME ";
|
||||
}
|
||||
if ((flags & MS_KERNMOUNT) != 0) {
|
||||
ss << "MS_KERNMOUNT ";
|
||||
}
|
||||
if ((flags & MS_I_VERSION) != 0) {
|
||||
ss << "MS_I_VERSION ";
|
||||
}
|
||||
if ((flags & MS_STRICTATIME) != 0) {
|
||||
ss << "MS_STRICTATIME ";
|
||||
}
|
||||
if ((flags & MS_LAZYTIME) != 0) {
|
||||
ss << "MS_LAZYTIME ";
|
||||
}
|
||||
if ((flags & MS_ACTIVE) != 0) {
|
||||
ss << "MS_ACTIVE ";
|
||||
}
|
||||
if ((flags & MS_NOUSER) != 0) {
|
||||
ss << "MS_NOUSER ";
|
||||
for (const auto &[flag, name] : mount_flags) {
|
||||
if ((flags & flag) != 0) {
|
||||
ss << name << " ";
|
||||
}
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
|
|
@ -256,8 +222,16 @@ void execute_hook(const linyaps_box::config::hooks_t::hook_t &hook)
|
|||
const_cast<char *const *>(c_args.data()), // NOLINT
|
||||
const_cast<char *const *>(c_env.data())); // NOLINT
|
||||
|
||||
std::cerr << "execvp: " << strerror(errno) << " errno=" << errno << std::endl;
|
||||
exit(1);
|
||||
LINYAPS_BOX_ERR() << "execute hook " << [&bin, &c_args]() -> std::string {
|
||||
std::stringstream stream;
|
||||
stream << bin;
|
||||
for (const auto &arg : c_args) {
|
||||
stream << " " << arg;
|
||||
}
|
||||
return std::move(stream).str();
|
||||
}() << " failed: "
|
||||
<< strerror(errno);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
|
|
@ -314,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -327,23 +308,24 @@ void syscall_mount(const char *_special_file,
|
|||
unsigned long int _rwflag,
|
||||
const void *_data)
|
||||
{
|
||||
constexpr decltype(auto) fd_prefix = "/proc/self/fd/";
|
||||
constexpr std::string_view fd_prefix = "/proc/self/fd/";
|
||||
LINYAPS_BOX_DEBUG() << "mount\n"
|
||||
<< "\t_special_file = " << [_special_file]() -> std::string {
|
||||
<< "\t_special_file = " << [_special_file, fd_prefix]() -> std::string {
|
||||
if (_special_file == nullptr) {
|
||||
return "nullptr";
|
||||
}
|
||||
if (auto str = std::string_view{ _special_file }; str.rfind(fd_prefix, 0) == 0) {
|
||||
return linyaps_box::utils::inspect_fd(std::stoi(str.data() + sizeof(fd_prefix) - 1));
|
||||
return linyaps_box::utils::inspect_fd(std::stoi(str.data() + fd_prefix.size()));
|
||||
}
|
||||
return _special_file;
|
||||
}() << "\n\t_dir = "
|
||||
<< [_dir]() -> std::string {
|
||||
<< [_dir, fd_prefix]() -> std::string {
|
||||
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() + sizeof(fd_prefix) - 1));
|
||||
return linyaps_box::utils::inspect_fd(std::stoi(str.data() + fd_prefix.size()));
|
||||
}
|
||||
return _dir;
|
||||
}() << "\n\t_fstype = "
|
||||
|
|
@ -357,10 +339,10 @@ void syscall_mount(const char *_special_file,
|
|||
if (_data == nullptr) {
|
||||
return "nullptr";
|
||||
}
|
||||
return reinterpret_cast<const char *>(_data);
|
||||
return static_cast<const char *>(_data);
|
||||
}();
|
||||
|
||||
int ret = ::mount(_special_file, _dir, _fstype, _rwflag, _data);
|
||||
auto ret = ::mount(_special_file, _dir, _fstype, _rwflag, _data);
|
||||
if (ret < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "mount");
|
||||
}
|
||||
|
|
@ -399,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<unsigned long>(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,
|
||||
|
|
@ -416,21 +400,12 @@ 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(
|
||||
const linyaps_box::utils::file_descriptor &root, const std::filesystem::path &destination)
|
||||
{
|
||||
LINYAPS_BOX_DEBUG() << "Creating file " << 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());
|
||||
}
|
||||
|
||||
[[nodiscard]] linyaps_box::utils::file_descriptor create_destination_directory(
|
||||
const linyaps_box::utils::file_descriptor &root, const std::filesystem::path &destination)
|
||||
{
|
||||
|
|
@ -439,12 +414,40 @@ void do_remount(const remount_t &mount)
|
|||
return linyaps_box::utils::mkdir(root, 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)
|
||||
{
|
||||
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());
|
||||
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
|
||||
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 "
|
||||
|
|
@ -470,13 +473,8 @@ create_destination_symlink(const linyaps_box::utils::file_descriptor &root,
|
|||
+ " already exists and is not a symlink");
|
||||
}
|
||||
|
||||
std::array<char, PATH_MAX + 1> 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<size_t>(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);
|
||||
}
|
||||
|
||||
|
|
@ -492,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) {
|
||||
|
|
@ -515,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,
|
||||
|
|
@ -555,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;
|
||||
|
|
@ -564,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");
|
||||
|
|
@ -683,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,
|
||||
|
|
@ -700,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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -728,20 +724,31 @@ public:
|
|||
|
||||
// we will pivot root later
|
||||
LINYAPS_BOX_DEBUG() << "configure rootfs";
|
||||
do_propagation_mount(linyaps_box::utils::open("/", O_PATH | O_CLOEXEC | O_DIRECTORY),
|
||||
MS_REC | MS_PRIVATE);
|
||||
auto flags = config.linux->rootfs_propagation;
|
||||
if ((flags & propagations_flag) == 0) {
|
||||
flags = MS_PRIVATE | MS_REC;
|
||||
}
|
||||
|
||||
// make sure the parent mount of rootfs is private
|
||||
// change the propagation type of rootfs mountpoint to configured type
|
||||
// otherwise bind mount will inherit the propagation type of rootfs mountpoint
|
||||
do_propagation_mount(linyaps_box::utils::open("/", O_PATH | O_CLOEXEC | O_DIRECTORY),
|
||||
flags);
|
||||
|
||||
// make sure the parent mountpoint of new root is private
|
||||
// pivot root will fail if it has shared propagation type
|
||||
make_rootfs_private();
|
||||
|
||||
// pivot root will reset the propagation type of rootfs mountpoint
|
||||
// we need to save the propagation type to make sure the parent mountpoint of new root is
|
||||
// what we want
|
||||
get_private_data(container).rootfs_propagation = flags;
|
||||
|
||||
LINYAPS_BOX_DEBUG() << "rebind container rootfs";
|
||||
|
||||
linyaps_box::config::mount_t mount;
|
||||
mount.source = root.current_path();
|
||||
mount.destination = ".";
|
||||
mount.flags = MS_BIND | MS_REC;
|
||||
// mount.propagation_flags = MS_PRIVATE | MS_REC;
|
||||
mount.flags = MS_BIND | MS_REC | MS_PRIVATE;
|
||||
auto ret = do_mount(container, root, mount);
|
||||
assert(!ret);
|
||||
|
||||
|
|
@ -789,6 +796,8 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
LINYAPS_BOX_DEBUG() << "make readonly paths";
|
||||
|
||||
for (const auto &path : *linux->readonly_paths) {
|
||||
linyaps_box::utils::file_descriptor dst;
|
||||
try {
|
||||
|
|
@ -835,10 +844,14 @@ public:
|
|||
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;
|
||||
|
|
@ -1024,14 +1037,16 @@ private:
|
|||
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -1047,27 +1062,13 @@ private:
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1097,7 +1098,9 @@ private:
|
|||
|
||||
try {
|
||||
auto path = destination.relative_path();
|
||||
linyaps_box::utils::mknodat(root, path, mode | type, dev);
|
||||
auto f_type = static_cast<unsigned int>(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();
|
||||
|
|
@ -1130,14 +1133,16 @@ private:
|
|||
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
|
||||
|
|
@ -1233,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<char *const *>(c_args.data()),
|
||||
const_cast<char *const *>(c_env.data()));
|
||||
const_cast<char *const *>(c_args.data()), // NOLINT
|
||||
const_cast<char *const *>(c_env.data())); // NOLINT
|
||||
|
||||
throw std::system_error(errno, std::generic_category(), "execvpe");
|
||||
}
|
||||
|
|
@ -1319,7 +1327,7 @@ void create_container_hooks(const linyaps_box::container &container,
|
|||
LINYAPS_BOX_DEBUG() << "Sync message sent";
|
||||
}
|
||||
|
||||
void do_pivot_root(const std::filesystem::path &rootfs)
|
||||
void do_pivot_root(const linyaps_box::container &container, const std::filesystem::path &rootfs)
|
||||
{
|
||||
LINYAPS_BOX_DEBUG() << "start pivot root";
|
||||
LINYAPS_BOX_DEBUG() << linyaps_box::utils::inspect_fds();
|
||||
|
|
@ -1344,6 +1352,15 @@ void do_pivot_root(const std::filesystem::path &rootfs)
|
|||
throw std::system_error(errno, std::generic_category(), "pivot_root");
|
||||
}
|
||||
|
||||
ret = fchdir(old_root.get());
|
||||
if (ret < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "fchdir");
|
||||
}
|
||||
|
||||
// make sure that umount event couldn't propagate to host
|
||||
do_propagation_mount(old_root, MS_REC | MS_PRIVATE);
|
||||
|
||||
// umount old root
|
||||
ret = umount2(".", MNT_DETACH);
|
||||
if (ret < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "umount2");
|
||||
|
|
@ -1363,6 +1380,10 @@ void do_pivot_root(const std::filesystem::path &rootfs)
|
|||
if (ret < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "chdir");
|
||||
}
|
||||
|
||||
// restore the propagation type of rootfs mountpoint
|
||||
do_propagation_mount(linyaps_box::utils::open("/", O_PATH | O_CLOEXEC | O_DIRECTORY),
|
||||
get_private_data(container).rootfs_propagation);
|
||||
}
|
||||
|
||||
void set_umask(const std::optional<mode_t> &mask)
|
||||
|
|
@ -1497,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");
|
||||
|
|
@ -1559,6 +1580,68 @@ void close_other_fds(std::set<uint> except_fds)
|
|||
}
|
||||
}
|
||||
|
||||
void processing_extensions(const linyaps_box::container &container)
|
||||
{
|
||||
const auto &config = container.get_config();
|
||||
|
||||
if (!config.annotations) {
|
||||
return;
|
||||
}
|
||||
|
||||
LINYAPS_BOX_DEBUG() << "Processing container extensions";
|
||||
|
||||
// ext_ns_last_pid
|
||||
// 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
|
||||
try {
|
||||
// Use std::stoll to handle larger ranges, then check if it fits in pid_t
|
||||
const auto value = std::stoll(it->second);
|
||||
if (value < 0 || value > std::numeric_limits<pid_t>::max()) {
|
||||
throw std::runtime_error("ns_last_pid value out of range: " + it->second
|
||||
+ " (must be between 0 and "
|
||||
+ std::to_string(std::numeric_limits<pid_t>::max()) + ")");
|
||||
}
|
||||
} catch (const std::out_of_range &e) {
|
||||
throw std::runtime_error("parse ns_last_pid " + it->second + " failed: " + e.what());
|
||||
} catch (const std::invalid_argument &e) {
|
||||
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(ns_last_pid);
|
||||
if (!ofs) {
|
||||
throw std::system_error(errno,
|
||||
std::generic_category(),
|
||||
"failed to open /proc/sys/kernel/ns_last_pid");
|
||||
}
|
||||
|
||||
ofs << it->second;
|
||||
if (!ofs) {
|
||||
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";
|
||||
}
|
||||
|
||||
void signal_USR1_handler([[maybe_unused]] int sig)
|
||||
{
|
||||
LINYAPS_BOX_DEBUG() << "Signal USR1 received:" << sig;
|
||||
|
|
@ -1589,8 +1672,16 @@ try {
|
|||
auto &args = *static_cast<clone_fn_args *>(data);
|
||||
|
||||
assert(args.socket.get() >= 0);
|
||||
close_other_fds(
|
||||
{ STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, (unsigned int)(args.socket.get()) });
|
||||
std::set<uint> 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<unsigned int>(args.socket.get()));
|
||||
close_other_fds(std::move(except_fds));
|
||||
|
||||
const auto &container = *args.container;
|
||||
const auto &process = *args.process;
|
||||
|
|
@ -1598,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);
|
||||
}
|
||||
|
||||
|
|
@ -1608,16 +1700,18 @@ try {
|
|||
wait_create_runtime_result(container, socket);
|
||||
create_container_hooks(container, socket);
|
||||
// TODO: selinux label/apparmor profile
|
||||
do_pivot_root(rootfs);
|
||||
do_pivot_root(container, rootfs);
|
||||
set_umask(container.get_config().process.user.umask);
|
||||
// processing all extensions before drop capabilities
|
||||
processing_extensions(container);
|
||||
set_capabilities(container, runtime_cap);
|
||||
start_container_hooks(container, socket);
|
||||
execute_process(process);
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
LINYAPS_BOX_ERR() << "clone failed: " << e.what();
|
||||
return -1;
|
||||
} catch (...) {
|
||||
std::cerr << "unknown error" << std::endl;
|
||||
LINYAPS_BOX_ERR() << "clone failed: unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1668,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1711,17 +1808,16 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
const auto code = errno;
|
||||
std::cerr << "munmap: " << strerror(code) << std::endl;
|
||||
LINYAPS_BOX_ERR() << "munmap child stack failed: " << strerror(errno);
|
||||
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<std::byte *>(this->stack_low) + LINYAPS_BOX_CLONE_CHILD_STACK_SIZE;
|
||||
} else {
|
||||
return (char *)this->stack_low - LINYAPS_BOX_CLONE_CHILD_STACK_SIZE;
|
||||
return static_cast<std::byte *>(this->stack_low) - LINYAPS_BOX_CLONE_CHILD_STACK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1764,14 +1860,14 @@ std::tuple<int, linyaps_box::utils::file_descriptor> 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");
|
||||
}
|
||||
|
|
@ -1819,12 +1915,9 @@ std::tuple<int, linyaps_box::utils::file_descriptor> start_container_process(
|
|||
}
|
||||
|
||||
c_args.push_back(nullptr);
|
||||
auto ret = execvp(c_args[0], const_cast<char *const *>(c_args.data()));
|
||||
if (ret < 0) {
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
execvp(c_args[0], const_cast<char *const *>(c_args.data()));
|
||||
LINYAPS_BOX_ERR() << "execute helper " << c_args[0] << " failed: " << strerror(errno);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
|
|
@ -2206,7 +2299,7 @@ void poststop_hooks(const linyaps_box::container &container) noexcept
|
|||
try {
|
||||
execute_hook(hook);
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
LINYAPS_BOX_ERR() << "execute poststop hook " << hook.path << " failed: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2237,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;
|
||||
}
|
||||
|
|
@ -2270,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;
|
||||
|
|
@ -2283,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<disabled_cgroup_manager>();
|
||||
} break;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
container(const status_directory &status_dir, const create_container_options_t &options);
|
||||
|
||||
[[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 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<cgroup_manager> manager;
|
||||
int preserve_fds_;
|
||||
gid_t host_gid_;
|
||||
uid_t host_uid_;
|
||||
container_data *data{ nullptr };
|
||||
std::filesystem::path bundle;
|
||||
std::unique_ptr<cgroup_manager> manager;
|
||||
linyaps_box::config config;
|
||||
};
|
||||
|
||||
} // namespace linyaps_box
|
||||
|
|
|
|||
|
|
@ -12,14 +12,16 @@
|
|||
#include <unistd.h>
|
||||
|
||||
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_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ struct container_status_t
|
|||
std::unordered_map<std::string, std::string> 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@
|
|||
#include "linyaps_box/utils/log.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <csignal> // IWYU pragma: keep
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
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<std::string>());
|
||||
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<std::string> linyaps_box::impl::status_directory::list() const
|
||||
auto linyaps_box::impl::status_directory::list() const -> std::vector<std::string>
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
for (const auto &entry : std::filesystem::directory_iterator(this->path)) {
|
||||
|
|
@ -95,8 +95,8 @@ std::vector<std::string> 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;
|
||||
|
|
|
|||
|
|
@ -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<std::string> list() const override;
|
||||
[[nodiscard]] auto list() const -> std::vector<std::string> override;
|
||||
|
||||
explicit status_directory(const std::filesystem::path &path);
|
||||
explicit status_directory(std::filesystem::path dir);
|
||||
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <iostream>
|
||||
|
||||
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<container_status_t> &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<int>(s.ID.length()));
|
||||
}
|
||||
|
||||
std::cout << std::left << std::setw(max_length + 1) << "NAME" << std::setw(10) << "PID"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<container_status_t> &status) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,27 +5,30 @@
|
|||
#include "linyaps_box/runtime.h"
|
||||
|
||||
linyaps_box::runtime_t::runtime_t(std::unique_ptr<linyaps_box::status_directory> &&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<std::string, linyaps_box::container_ref> linyaps_box::runtime_t::containers()
|
||||
auto linyaps_box::runtime_t::containers()
|
||||
-> std::unordered_map<std::string, linyaps_box::container_ref>
|
||||
{
|
||||
auto container_ids = this->status_dir->list();
|
||||
auto container_ids = this->status_dir_->list();
|
||||
|
||||
std::unordered_map<std::string, container_ref> 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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,20 +17,12 @@ class runtime_t
|
|||
{
|
||||
public:
|
||||
explicit runtime_t(std::unique_ptr<status_directory> &&status_dir);
|
||||
std::unordered_map<std::string, container_ref> containers();
|
||||
auto containers() -> std::unordered_map<std::string, container_ref>;
|
||||
|
||||
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_directory> status_dir;
|
||||
std::unique_ptr<status_directory> status_dir_;
|
||||
};
|
||||
|
||||
} // namespace linyaps_box
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<std::string> list() const = 0;
|
||||
[[nodiscard]] virtual auto list() const -> std::vector<std::string> = 0;
|
||||
};
|
||||
} // namespace linyaps_box
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<uint>(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
|
||||
|
|
@ -54,11 +54,12 @@ void close_range_fallback(uint first, uint last, int flags)
|
|||
|
||||
struct dirent *next{ nullptr };
|
||||
while ((next = ::readdir(dir)) != nullptr) {
|
||||
if (next->d_name[0] == '.') { // skip "." and ".."
|
||||
const std::string name{ next->d_name[0] };
|
||||
if (name == "." || name == "..") { // skip "." and ".."
|
||||
continue;
|
||||
}
|
||||
|
||||
auto fd = std::stoi(next->d_name);
|
||||
auto fd = std::stoi(name);
|
||||
if (fd == self_fd) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -67,18 +68,15 @@ void close_range_fallback(uint first, uint last, int flags)
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((flags & CLOSE_RANGE_CLOEXEC) != 0) {
|
||||
if ((static_cast<uint>(flags) & CLOSE_RANGE_CLOEXEC) != 0) {
|
||||
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
|
||||
throw std::system_error(errno,
|
||||
std::generic_category(),
|
||||
std::string{ "failed to set up close-on-exec to " }
|
||||
+ next->d_name);
|
||||
"failed to set up close-on-exec to " + name);
|
||||
}
|
||||
} else {
|
||||
if (::close(fd) < 0) {
|
||||
throw std::system_error(errno,
|
||||
std::generic_category(),
|
||||
std::string{ "failed to close " } + next->d_name);
|
||||
throw std::system_error(errno, std::generic_category(), "failed to close " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,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<uint>(flags) & CLOSE_RANGE_CLOEXEC) != 0) {
|
||||
ss << "CLOSE_RANGE_CLOEXEC ";
|
||||
}
|
||||
if ((flags & CLOSE_RANGE_UNSHARE) != 0) {
|
||||
if ((static_cast<uint>(flags) & CLOSE_RANGE_UNSHARE) != 0) {
|
||||
ss << "CLOSE_RANGE_UNSHARE ";
|
||||
}
|
||||
ss << ']';
|
||||
|
|
|
|||
|
|
@ -26,16 +26,15 @@ template<typename Fn,
|
|||
struct defer
|
||||
{
|
||||
explicit defer(Fn &&fn) noexcept
|
||||
: fn(std::move(fn))
|
||||
, cancelled(false)
|
||||
: fn_(std::move(fn))
|
||||
{
|
||||
}
|
||||
|
||||
defer(const defer &) = delete;
|
||||
defer &operator=(const defer &) = delete;
|
||||
auto operator=(const defer &) -> 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
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "linyaps_box/utils/file_describer.h"
|
||||
|
||||
#include "linyaps_box/utils/fstat.h"
|
||||
#include "linyaps_box/utils/log.h"
|
||||
|
||||
#include <cstring>
|
||||
|
|
@ -16,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,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");
|
||||
}
|
||||
|
|
@ -75,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;
|
||||
}
|
||||
|
|
@ -90,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;
|
||||
}
|
||||
|
|
@ -115,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();
|
||||
|
|
@ -132,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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ public:
|
|||
~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
|
||||
|
|
|
|||
|
|
@ -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 <cassert>
|
||||
|
||||
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<int>(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
|
||||
|
|
|
|||
|
|
@ -12,41 +12,14 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
namespace linyaps_box::utils {
|
||||
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;
|
||||
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
|
@ -21,14 +20,14 @@
|
|||
namespace linyaps_box::utils {
|
||||
|
||||
template<unsigned int level>
|
||||
Logger<level>::~Logger()
|
||||
Logger<level>::~Logger() noexcept
|
||||
{
|
||||
if (level > get_current_log_level()) {
|
||||
return;
|
||||
}
|
||||
|
||||
flush();
|
||||
auto str = this->str();
|
||||
ss.flush();
|
||||
auto str = ss.str();
|
||||
|
||||
syslog(level, "%s", str.c_str());
|
||||
|
||||
|
|
@ -69,31 +68,32 @@ template class Logger<LOG_NOTICE>;
|
|||
template class Logger<LOG_INFO>;
|
||||
template class Logger<LOG_DEBUG>;
|
||||
|
||||
bool force_log_to_stderr()
|
||||
auto force_log_to_stderr() -> bool
|
||||
{
|
||||
static auto *result = getenv("LINYAPS_BOX_LOG_FORCE_STDERR");
|
||||
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 bool result = isatty(fileno(stderr)) != 0;
|
||||
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<unsigned int>(ret);
|
||||
if (level > LOG_DEBUG) {
|
||||
return LOG_DEBUG;
|
||||
}
|
||||
|
|
@ -102,15 +102,15 @@ unsigned int get_current_log_level_from_env()
|
|||
}
|
||||
} // namespace
|
||||
|
||||
unsigned int get_current_log_level()
|
||||
auto get_current_log_level() -> unsigned int
|
||||
{
|
||||
static unsigned int level = get_current_log_level_from_env();
|
||||
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
|
||||
{
|
||||
std::string pidns_path = "/proc/" + ((pid != 0) ? std::to_string(pid) : "self") + "/ns/pid";
|
||||
const auto &pidns_path = "/proc/" + ((pid != 0) ? std::to_string(pid) : "self") + "/ns/pid";
|
||||
|
||||
std::array<char, PATH_MAX + 1> buf{};
|
||||
auto length = ::readlink(pidns_path.c_str(), buf.data(), PATH_MAX);
|
||||
|
|
@ -118,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<size_t>(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.back() != ']') {
|
||||
std::abort();
|
||||
if (result.rfind(prefix, 0) != 0) {
|
||||
return "invalid format";
|
||||
}
|
||||
|
||||
return result.substr(sizeof("pid:[") - 1, result.size() - 6);
|
||||
if (result.back() != suffix) {
|
||||
return "invalid format";
|
||||
}
|
||||
|
||||
return std::string{ result.substr(prefix_len, result.size() - total_wrapper_len) };
|
||||
}
|
||||
|
||||
} // namespace linyaps_box::utils
|
||||
|
|
|
|||
|
|
@ -13,18 +13,41 @@
|
|||
// 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<unsigned int level>
|
||||
class Logger : public std::stringstream
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
using std::stringstream::stringstream;
|
||||
~Logger() override;
|
||||
Logger() = default;
|
||||
Logger(const Logger &) = delete;
|
||||
auto operator=(const Logger &) -> Logger & = delete;
|
||||
Logger(Logger &&) noexcept(std::is_nothrow_move_constructible_v<std::ostringstream>) = // NOLINT
|
||||
default;
|
||||
Logger &
|
||||
operator=(Logger &&) noexcept(std::is_nothrow_move_assignable_v<std::ostringstream>) = // NOLINT
|
||||
default;
|
||||
~Logger() noexcept;
|
||||
|
||||
template<typename T>
|
||||
auto operator<<(const T &value) -> Logger &
|
||||
{
|
||||
ss << value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator<<(std::ostream &(*manipulator)(std::ostream &)) -> Logger &
|
||||
{
|
||||
manipulator(ss);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream ss;
|
||||
};
|
||||
|
||||
extern template class Logger<LOG_EMERG>;
|
||||
|
|
|
|||
|
|
@ -12,9 +12,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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());
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
#include "linyaps_box/utils/file_describer.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <filesystem>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace linyaps_box::utils {
|
||||
void mknodat(const file_descriptor &root,
|
||||
const std::filesystem::path &path,
|
||||
|
|
|
|||
|
|
@ -24,19 +24,19 @@
|
|||
#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<size_t>(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
|
||||
auto file_path = path.relative_path();
|
||||
int fd = ::openat(root.get(), file_path.c_str(), flag, mode);
|
||||
const auto &file_path = path.relative_path();
|
||||
const auto fd = ::openat(root.get(), file_path.c_str(), flag, mode);
|
||||
if (fd < 0) {
|
||||
auto full_path = root.current_path() / path.relative_path();
|
||||
throw std::system_error(errno,
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -57,7 +57,7 @@ syscall_openat2(int dirfd, const char *path, uint64_t flag, uint64_t mode, uint6
|
|||
uint64_t resolve;
|
||||
} how{ flag, mode, resolve };
|
||||
|
||||
auto ret = syscall(__NR_openat2, dirfd, path, &how, sizeof(openat2_how), 0);
|
||||
const auto ret = syscall(__NR_openat2, dirfd, path, &how, sizeof(openat2_how), 0);
|
||||
if (ret < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "openat2");
|
||||
}
|
||||
|
|
@ -66,12 +66,12 @@ 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);
|
||||
int fd = ::open(path.c_str(), flag, mode);
|
||||
LINYAPS_BOX_DEBUG() << "open " << path.c_str() << " with "
|
||||
<< inspect_fcntl_or_open_flags(static_cast<size_t>(flag));
|
||||
const auto fd = ::open(path.c_str(), flag, mode);
|
||||
if (fd == -1) {
|
||||
throw std::system_error(errno,
|
||||
std::generic_category(),
|
||||
|
|
@ -81,21 +81,25 @@ 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<size_t>(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<uint64_t>(flag),
|
||||
mode,
|
||||
RESOLVE_IN_ROOT);
|
||||
} catch (const std::system_error &e) {
|
||||
auto code = e.code().value();
|
||||
const auto code = e.code().value();
|
||||
if (code == EINTR || code == EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
#include <sys/resource.h>
|
||||
|
||||
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<std::string_view, int> sigMap{
|
||||
const std::unordered_map<std::string_view, int> 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<std::string_view, int> resources{
|
||||
const std::unordered_map<std::string_view, int> resources{
|
||||
{ "RLIMIT_AS", RLIMIT_AS },
|
||||
{ "RLIMIT_CORE", RLIMIT_CORE },
|
||||
{ "RLIMIT_CPU", RLIMIT_CPU },
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
#include <string_view>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<unsigned int>(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<unsigned int>(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<unsigned int>(ret) };
|
||||
|
||||
if (end == std::string::npos) {
|
||||
this->major_ = major;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ public:
|
|||
|
||||
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]] 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]] std::string to_string() const;
|
||||
[[nodiscard]] bool is_compatible_with(const semver &other) const;
|
||||
[[nodiscard]] auto to_string() const -> std::string;
|
||||
[[nodiscard]] auto is_compatible_with(const semver &other) const -> bool;
|
||||
|
||||
private:
|
||||
unsigned int major_;
|
||||
|
|
|
|||
|
|
@ -3,3 +3,23 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "linyaps_box/utils/socketpair.h"
|
||||
|
||||
#include <array>
|
||||
#include <system_error>
|
||||
|
||||
namespace linyaps_box::utils {
|
||||
|
||||
auto socketpair(int domain, int type, int protocol) -> std::pair<file_descriptor, file_descriptor>
|
||||
{
|
||||
std::array<int, 2> 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
|
||||
|
|
|
|||
|
|
@ -4,25 +4,11 @@
|
|||
|
||||
#include "linyaps_box/utils/file_describer.h"
|
||||
|
||||
#include <array>
|
||||
#include <system_error>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
namespace linyaps_box::utils {
|
||||
|
||||
inline std::pair<file_descriptor, file_descriptor> socketpair(int domain, int type, int protocol)
|
||||
{
|
||||
std::array<int, 2> 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<file_descriptor, file_descriptor>;
|
||||
|
||||
} // namespace linyaps_box::utils
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include "linyaps_box/utils/log.h"
|
||||
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
void linyaps_box::utils::symlink(const std::filesystem::path &target,
|
||||
const std::filesystem::path &link_path)
|
||||
{
|
||||
|
|
@ -25,8 +29,31 @@ void linyaps_box::utils::symlink_at(const std::filesystem::path &target,
|
|||
LINYAPS_BOX_DEBUG() << "Create symlink " << link_path << " which under " << dirfd.current_path()
|
||||
<< " point to " << target;
|
||||
|
||||
auto ret = ::symlinkat(target.c_str(), dirfd.get(), link_path.c_str());
|
||||
const auto ret = ::symlinkat(target.c_str(), dirfd.get(), link_path.c_str());
|
||||
if (ret == -1) {
|
||||
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<char, PATH_MAX + 1> 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() };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,14 @@
|
|||
|
||||
namespace linyaps_box::utils {
|
||||
|
||||
void symlink(const std::filesystem::path &source, const std::filesystem::path &target);
|
||||
void symlink(const std::filesystem::path &target, const std::filesystem::path &link_path);
|
||||
|
||||
void symlink_at(const std::filesystem::path &target,
|
||||
const file_descriptor &dirfd,
|
||||
const std::filesystem::path &link_path);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
#include <fcntl.h>
|
||||
|
||||
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());
|
||||
int 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(),
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@
|
|||
|
||||
#include "linyaps_box/utils/file_describer.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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)"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
Loading…
Reference in New Issue