Compare commits

...

191 Commits

Author SHA1 Message Date
ComixHe 5a4eaff29f fix: explicitly cast MS_NOUSER to unsigned int
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-21 17:22:44 +08:00
ComixHe e12e421143 chore: Begin development of 2.1.3
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-21 16:28:37 +08:00
ComixHe caf2cec0d2 refactor: Add conditional compilation protection for SIGCLD signal
SIGCLD is a legacy signal in System V UNIX.
Mapping it with #ifdef SIGCLD in str_to_signal function
to improve cross-platform compatibility and avoid compilation errors
on platforms where SIGCLD is not defined (e.g. musl).

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-20 16:40:05 +08:00
ComixHe 59ed24f214 refactor: add <sys/types.h> for mode_t
- fix inconsistencies between function parameter implementations and declared types.
- change the default permission of touched file.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-20 16:40:05 +08:00
ComixHe 27c0ae9643 refactor: remove __S_ISTYPE for improving portability
__S_ISTYPE is glibc internal macro.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-20 16:40:05 +08:00
ComixHe 8f1b042421 refactor: change the type of mount flag to unsigned int
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-20 16:40:05 +08:00
ComixHe 613cc27af6 refactor: standardize preserve_fds type for better portability
Change preserve_fds from uint to int type

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-20 16:40:05 +08:00
ComixHe 56bb0d1e6b build: CPM is disabled by default
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-15 15:35:07 +08:00
ComixHe 72ca241a20 chore: update CPM.cmake to 0.42.0
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-15 15:35:07 +08:00
ComixHe 11face2e94 chore: formatting codes
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-13 11:28:59 +08:00
ComixHe 04aface617 chore(tools): add clang-format script for formatting C++ sources
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-13 11:28:59 +08:00
ComixHe 08b790480d feat: allow cn.org.linyaps.runtime.ns_last_pid extension failed
if /proc/sys/kernel/ns_last_pid doesn't exist, skip this extension.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-13 11:28:59 +08:00
deepsource-autofix[bot] 53f0e1e835 style: format code with ClangFormat and Prettier
This commit fixes the style issues introduced in 881bcc9 according to the output
from ClangFormat and Prettier.

Details: None
2025-10-13 10:57:55 +08:00
ComixHe 881bcc93c0 fix: move the one of sockpairs to child container process
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-10-09 15:20:07 +08:00
ComixHe 0e5def0fe2 refactor: change the default open flag
make O_PATH as the default flag.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-09-15 14:32:46 +08:00
ComixHe 9204ffba0c fix: use O_PATH flag when masking paths to avoid permission issues
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-09-15 14:21:39 +08:00
ComixHe 3b94fbcf43 fix(deps): upgrade CLI11 to 2.5.0 to resolve bug in argument parsing
See 7ff65c16f2

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-09-11 17:59:04 +08:00
ComixHe 4ad30a7efb fix: correct variable name
Fixes #115

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-09-05 14:16:53 +08:00
ComixHe 24a6a99f98 feat: implement preserve_fds support and fix file descriptor handling
- Add --preserve-fds command line option for runtime
- Refactor container creation with unified options structure
- Improve file_descriptor class API

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-09-01 14:13:21 +08:00
ComixHe b1c7fa13d8 fix: handle broken symlinks during mount destination creation
When a destination file doesn't exist, the previous implementation couldn't
distinguish between creation failure due to existing files vs other reasons.
If both opening and creating fail, the destination is definitely a broken symlink.

- Add recursive symlink resolution with depth limit (32) in create_destination_file()
- Use O_NOFOLLOW to detect symlink during file creation
- When creation fails with ELOOP, read symlink target and recursively create it
- Replace filesystem functions with internal utils for consistent error handling
- Add proper broken symlink detection and resolution logic

This ensures mount destinations work correctly even when they point to
broken symlinks, by creating the missing target files in the symlink chain.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-29 17:51:36 +08:00
ComixHe 08c47fff3c refactor: resolving the most of compiler warnings
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-26 14:45:06 +08:00
ComixHe e62d7d53e4 fix: early return if an exception is thrown while command parsing
According the documentation of CLI11, this library will throw a exception
while encounter -h,--help or a parse error.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-25 10:44:11 +08:00
ComixHe 2b51656a6e chore: suppress compiler warning
If the log level is higher than debug level
the dump_mount_flags function will not be called.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-25 10:29:21 +08:00
taotieren 2a8c21b681 docs: Update linyaps-box documentation
1.  Update linyaps-box package status

docs: 更新 linyaps-box文档

1.  更新 linyaps-box 软件包状态
2025-08-20 15:27:24 +08:00
ComixHe 61d1b26eae feat: add ns_last_pid extension test and enhance test framework
- Add comprehensive test for ns_last_pid extension
- Improve test script JSON merging capabilities
- Update existing tests and CMake configuration

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-06 11:53:44 +08:00
ComixHe 53883d5bf4 improve: add system error details to file operation error handling
Replace std::runtime_error with std::system_error for better debugging.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-06 11:53:44 +08:00
ComixHe 55a147033b chore: bump version to 2.1.0
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-06 10:59:45 +08:00
ComixHe 308b80bacf feat: add rootfsPropagation support and improve error handling
- Add rootfsPropagation field to linux config (shared/slave/private/unbindable)
- Fix mount propagation flag handling (use |= instead of &=)
- Replace std::cerr with LINYAPS_BOX_ERR() for consistent logging
- Use _exit() instead of exit() in child processes
- Improve error messages with better context

Allows control over mount propagation for the root filesystem according
to OCI spec.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-08-05 15:43:50 +08:00
ComixHe 16f40a416a fix: resolve GCC enum conversion error in MountFlag
Use std::underlying_type_t to fix "int cannot be converted to unnamed enum"
compilation error on older GCC versions.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-23 13:47:39 +08:00
ComixHe 3951cfc9b2 fix: some warnings
- non-const global variable
- avoid array-to-pointer decay

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-23 13:23:47 +08:00
ComixHe 0bb8d07bb2 feat: add runtime extension 'cn.org.linyaps.runtime.ns_last_pid'
Add support for the 'cn.org.linyaps.runtime.ns_last_pid' runtime extension
that allows setting the last PID in the namespace during container startup.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-23 13:23:47 +08:00
ComixHe ee4e07a827 refactor: resolve some warnings from static check
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-23 11:38:07 +08:00
ComixHe e31f62f382 build: specifying the internal library type explicitly
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-21 10:17:01 +08:00
dengbo 000fa7ff73 chore: add deepsource config
Add deepsource config.
2025-07-18 14:50:46 +08:00
ComixHe 1fd78ccf1a chore: update CMake preset
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-18 11:15:47 +08:00
ComixHe f30bf60148 build: add an option to generate version
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-17 21:25:25 +08:00
ComixHe aa58f981ef fix: 'cgroup-manager' is not assigned while creating container
make 'disabled' as the default value of field 'cgroup-manager'

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-17 21:11:20 +08:00
ComixHe 489cc336e4 fix: correct ptmx source location
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-14 14:59:23 +08:00
ComixHe 35ac00a66a feat: support runtime Dev symbolic link
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-01 20:10:47 +08:00
ComixHe 9d524a908c feat: bind mount /dev/pts/ptmx to /dev/ptmx
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-01 20:10:47 +08:00
ComixHe 46ff1b817f fix: correct the default value of open_at parameter 'mode'
if mode is nonzero, but how.flags does not contain O_CREAT or O_TMPFILE
will cause an EINVAL.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-07-01 17:32:51 +08:00
ComixHe e714f88ba3 chore: update CMakePresets
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-20 17:19:55 +08:00
ComixHe 995231effc feat: add option '--env' for exec
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-17 16:24:06 +08:00
ComixHe 123cfeb539 chore: add version information for vendoring dependencies
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-13 13:12:28 +08:00
ComixHe 9887a351c4 fix: remove nsenter option '--wdns'
old nsenter command doesn't support '--wdns'.
remove this option for now.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-12 11:24:03 +08:00
ComixHe 37f8126145 fix: correct fallback syscall number
__NR_close_range should be 436.
include sys/syscall.h instead of asm/unistd.h.

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-12 11:00:29 +08:00
ComixHe bcd7063161 Revert "fix: do not define syscall number directly"
This reverts commit 2cb58b3a0f.
2025-06-12 11:00:29 +08:00
Iceyer 16ed42928d fix: correct output flag name in inspect_fcntl_or_open_flags
The function was incorrectly outputting 'O_SYNC' when checking for
the O_NDELAY flag. This was likely a copy-paste error that could
cause confusion when debugging file descriptor flags.

The issue was in the inspect_fcntl_or_open_flags function where
the condition checked for O_NDELAY but output the wrong flag name.

Changed:
- Fixed output from 'O_SYNC' to 'O_NDELAY' to match the actual flag being checked
2025-06-12 09:31:21 +08:00
ComixHe 2cb58b3a0f fix: do not define syscall number directly
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-11 18:17:20 +08:00
ComixHe cc49934702 fix: avoid dead loop in close_range_fallback
add helper class 'defer'

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-06-04 11:48:34 +08:00
ComixHe d2b7adbb1d feat: add flag 'mount_dev_from_host'
skip processing default devices if user binds '/dev' from host

Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-05-30 17:56:03 +08:00
ComixHe 4f8ad981d5 build: add public definition 'LINYAPS_BOX_STATIC_LINK' to library target
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-05-30 16:19:26 +08:00
ComixHe 594bedcf1c refactor: add version flag
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
2025-05-23 14:32:44 +08:00
ComixHe 25fdeda582 refactor: try single mapping at first if it could be
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-05-08 15:32:36 +08:00
ComixHe 6b9422ff84 refactor: remove unneed shared_ptr
improve open/open_at

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-30 17:40:13 +08:00
ComixHe 4d3d0002f2 test: ignore deprecated warning from libstdc++
refer: 5c34f02ba7

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-30 16:42:57 +08:00
ComixHe f1378216b3 chore: correct log
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-30 12:10:53 +08:00
ComixHe 22c8a34f3a test: add smoke test 'output-to-null'
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-28 16:41:07 +08:00
ComixHe 599b433142 refactor: output file path when failed to open or create it
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-25 17:09:13 +08:00
ComixHe 06f3a86652 refactor: try to create devices at first
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-25 16:16:02 +08:00
ComixHe 0bed97d189 fix: remove MS_NODEV option for /dev
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-25 13:28:02 +08:00
ComixHe e83566c4b4 refactor: ignore 'owner' field when box is linked statically
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-24 17:05:52 +08:00
ComixHe cc3db26490 Revert "fix: remove 'owner' from container state file"
This reverts commit 0cb72a0b1a.

linyaps needs this field.
2025-04-24 17:05:52 +08:00
ComixHe 0cb72a0b1a fix: remove 'owner' from container state file
we need support linking statically but calling
getpwuid from a statically linked program may cause
a segment fault.

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-21 15:57:53 +08:00
ComixHe 2f74ccca81 fix: the argument 'argv' and 'envp' of execvpe shouldn't be nullptr
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-21 15:57:53 +08:00
ComixHe fda6b3954d build: specifying labmda return type
compatible with old gcc

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-18 17:35:22 +08:00
ComixHe adcb988fa1 fix: correct args of executing hook
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-16 17:59:18 +08:00
ComixHe 26e24766d0 fix: splitting prestart hook and createRuntime hook into two steps
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-16 16:27:38 +08:00
ComixHe a1aad2f284 fix: correct hooks type
'args' and 'env' are optional in hooks

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-16 14:46:57 +08:00
ComixHe 08305e83d0 fix: correct bind mount flags of the configure_device fallback
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-16 12:23:39 +08:00
ComixHe f6661b83bf chore: update dependency 'nlohmann_json'
- nlohmann_json 3.12.0 released in 2025-04-11
  - change the minimal dependencies version

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-15 10:51:14 +08:00
ComixHe cf2e547d09 refactor: compatible with libcap 2.25
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-02 16:32:07 +08:00
ComixHe 23414a6aab refactor: change the way of feature detecting
- compatible with older gcc and cmake
  - remove configuration

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-02 15:04:39 +08:00
ComixHe 7d678a48ba build: change the compatible way of finding GTest in older cmake
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-02 13:37:00 +08:00
ComixHe 60b31b423e build: vendoring CLI11 and nlohmann_json
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-01 17:36:48 +08:00
ComixHe c6725bd393 chore: use macro instead of if constexpr
compatible with old gcc

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-31 17:58:49 +08:00
ComixHe e28caa9340 chore: adjust build process when disable cap and seccomp
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-31 17:40:11 +08:00
ComixHe c01c7bf36d refactor: implement close_range by ourself
compatible with old kernel and glibc

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-31 16:55:08 +08:00
ComixHe ed9fb921e8 refactor: improve openat2 implementation
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-31 16:55:08 +08:00
ComixHe 17c3827688 fix: fcntl is only used to set flags to a duplicated fd
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-28 17:37:00 +08:00
ComixHe 7b60087a2e fix: compatible with older libstdc++
DR: https://cplusplus.github.io/LWG/lwg-defects.html#1203
libstdc++ resolved commit: https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=a87ceadf17b4a899f3e74e2da8b6b209461d2742

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-27 17:45:10 +08:00
ComixHe 301aced752 chore: update reuse file
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-26 17:55:44 +08:00
ComixHe 2f0b801885 build: support fetching deps by CPM
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-26 17:55:44 +08:00
ComixHe c3590bc7b0 build: fix static build
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-26 17:55:44 +08:00
ComixHe 1a07d78884 chore: remove gdwarf64 for compatibility
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-26 16:37:01 +08:00
ComixHe 88f8f97465 feat: support readonly root
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-26 16:15:55 +08:00
ComixHe 1cb3bad58d refactor: box subcommand
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-20 13:07:18 +08:00
ComixHe 3a51dab444 refactor: improve copy-symlink error message
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-19 05:12:05 +00:00
ComixHe 6c71a61249 fix: config.json location
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-19 05:12:05 +00:00
ComixHe 377ff9c4c1 feat: support masked path
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-18 10:02:44 +00:00
ComixHe 95ac068ed0 feat: support readonly path
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-18 08:09:14 +00:00
ComixHe 2f70c75e51 feat: support setting process oom_score_adj
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-17 07:55:20 +00:00
ComixHe 6f4ddaefe5 test: adjust smoking test
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-17 07:03:51 +00:00
ComixHe 1cf359e14d feat: support set rlimits
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-17 05:43:08 +00:00
ComixHe 813bc6b072 feat: support umask
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-17 03:31:40 +00:00
ComixHe f1856a5404 test: adjust smoke tests
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-17 02:48:35 +00:00
ComixHe 7e74723197 test: adjust smoking test
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-14 09:10:40 +00:00
ComixHe 2586753235 feat: initialize exec capability support
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-14 07:03:55 +00:00
ComixHe 41c02f7c6f chore: format REUSE.toml
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-14 05:31:45 +00:00
ComixHe c7de95af42 feat: support set container capability
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-14 05:31:45 +00:00
Comix 197086419e
add format file and fix issue (#37)
* chore: add format file

  - add .editorconfig
  - add .cmake-format.py
  - cmake 'dev' preset use 'build-dev' as build directory

Signed-off-by: ComixHe <heyuming@deepin.org>

* fix: correct log marco definition

Signed-off-by: ComixHe <heyuming@deepin.org>

---------

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-11 15:46:49 +08:00
ComixHe c02e41b180 fix: nsenter report setgroups permission denied
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-10 21:23:29 +08:00
ComixHe b983d5f513 fix: subcommand exec parsing error
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-10 20:49:33 +08:00
ComixHe e2f473d44e refactor: container status structure
suppress memory leak caused by glib

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-10 19:30:42 +08:00
ComixHe 3c83d98431 refactor: command kill
support killing by specific signal

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-10 16:00:16 +08:00
ComixHe d2944e2ef5 refactor: adjust remounting process
- use a workwound for /sys/fs/cgroup
 - adjust the sequence of mounting process
 - adjust mount propagation process

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-10 13:52:13 +08:00
ComixHe e86a05384b feat: support extra mount flag 'copy-symlink'
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-06 21:12:27 +08:00
ComixHe 7200a91460 refactor: adjust the behavior of mount process
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-06 14:28:21 +08:00
ComixHe 125037414d feat: add global flag `cgroup-manager`
Initial support for the cgroup utility

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-04 17:36:23 +08:00
ComixHe 49f19a7347 fix: correct the type of clone flag
fix compile warning

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-03-04 17:25:31 +08:00
ComixHe 77ad719204 refactor: apply some clangd suggestion
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-02-28 17:37:20 +08:00
ComixHe c5f966ead7 chore: update reuse and SPDX comment
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-02-27 20:37:19 +08:00
black-desk c942000057 refactor: rewrite box
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-02-27 20:37:19 +08:00
black-desk d576be09ab
ci: remove auto-tag
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:27:51 +08:00
black-desk 351a33f768
chore: reuse lint
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:15 +08:00
black-desk 910898b337
fix: check write uid_map result
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:14 +08:00
black-desk d4c50ccc10
fix: log to stderr
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:13 +08:00
black-desk 65f24e63f3
build: add CMAKE_CXX_LAUNCHER to presets
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:12 +08:00
black-desk 9f80f26da0
tests: add coverage support
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:11 +08:00
black-desk 411741b306
tests: add smoke tests
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:10 +08:00
black-desk 2f76093cf1
fix: avoid missing mount point
76f9b3c62b
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:09 +08:00
black-desk 812b6770a5
chore: update ll-box help output
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:08 +08:00
black-desk 0e76506d5c
chore: remove debian diretory
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:07 +08:00
black-desk 1f19c3df1c
fix(seccomp): avoid memory leak
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:06 +08:00
black-desk bcebd8da68
fix: fix failed tests
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:05 +08:00
black-desk ff0cd07088
fix: avoid compiler warnings
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:04 +08:00
black-desk b642bf942c
build: refactory CMakeLists
Signed-off-by: black-desk <me@black-desk.cn>
2024-11-11 16:26:00 +08:00
black-desk 42b2907e63 ci: remove CLA check
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
ComixHe dc3f39b0f8 fix(box): adjust 'nosymfollow' priority to highest
when declare option 'copy-symlink' and 'nosymfollow' at the same time,
box will apply 'nosymfollow' and ignore 'copy-symlink'.

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
dengbo11 9f7f969d92 chore: adujust include guards
Use #pragma once to replace #ifndef
reference https://includeguardian.io/article/pragma-once-vs-ifndef.

Log: use #pragma once to guard include
2024-09-26 19:07:00 +08:00
ComixHe dbf51df551 fix(ll-box): use /proc/<pid>/stat to find the target pid
add a fallback for compatible with linux kernel which compiled with CONFIG_PROC_CHILDREN=false

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe e3a58f2a6d feat: support propagation bind
tweak remount implementation

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe 91f7f38e2c fix(ll-box): ignore remount temporarily when declaring nosymfollow
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe b0b4798a95 feat: add `nosymfollow` option
implementation mount a fd path to destination
instead of passing the MS_NOSYMFOLLOW directly.

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
kamiyadm 996f82e702 fix: failed to execute 'll-cli enter'
Remove '&' from exec() in ll-box. We add it in ll-cli.
issue: https://github.com/linuxdeepin/developer-center/issues/9445

Log:
2024-09-26 19:07:00 +08:00
black-desk ba28975e56 refact(box): clean warnings
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
ComixHe 930fce61ae fix: return -1 expliclitly at the end of function DoWait
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
kamiyadm da89d5c824 fix: failed to run app after ll-cli reviced SIGINT
* If the errno which returns from ::wait in ll-cli
  is EINTR, wait child more times. And if interrupt
  by SIGINT one more time, kill child immediately.
* If the pid which in container json  is not exists,
  remove the json.

Log:
2024-09-26 19:07:00 +08:00
kamiyadm 8708e52b43 fix: missed some env variables when reusing container
* Save env variables to /run/user/1000/linglong/containerId/00env.sh
  and mount it to /etc/profile.d/00env.sh in container.
* Remove 'bash --login -c' from exec() of ll-box, we
  add it before .

Log:
2024-09-26 19:07:00 +08:00
ComixHe be356f816c fix: create ll-host under tmpfs
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe b61bac988b refactor: checking some operations of ll-box are exectuing successfully or not
if someting goes wrong, return early

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe 16c15089c1 fix: couldn't find binary in container
improve parsing args

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe 15e53329c7 feat: reuse container when application is runnnig
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe bf9ab07f12 refactor(ll-box): arguments parsing
support exec and some options

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
black-desk d3397eefbb build: add missing header
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
ComixHe 03e772276a feat: support remount with fd
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
ComixHe 67bf6e9d97 feat: support copy-symlink
Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
black-desk ed4f87e317 Revert "fix: add finalizeMounts to delay remount"
This reverts commit 43a60681638bc56851f6fada952283bd508e3a8c.

Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk c98ca73207 Revert "fix(box): move finalize mounts to beginning of pivot root"
This reverts commit 1cc308e8598e9a665f17aa9645034a88258289f3.

Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 55968443bc fix(box): correct spelling
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk a5e55438d7 fix(box): default log level set to warning
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 04170fd370 fix(box): move finalize mounts to beginning of pivot root
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
ComixHe 5596756555 fix: add finalizeMounts to delay remount
and fix some typo

Issue: https://github.com/linuxdeepin/linglong/issues/398

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-09-26 19:07:00 +08:00
myml df1a816251 fix: build failure on UOS 20
修复在UOS 20系统构建错误

Log:
2024-09-26 19:07:00 +08:00
black-desk 0ac243071c fix(ll-box): exit with -1 when init don't return 0
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 0c015a4e59 feat(ll-box): initial implementation of `list` and `kill`
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk ead51959f3 chore: remove a debug log
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 90252e6d1d fix: void use after move
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 56ae579d61 fix: correct hook exec
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 3929bd38c9 fix: correct second clone id mapping
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk dcbbb53a8b feat: support startContainer
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 3a0446ed07 refact(ll-box): void unknow env
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 5f1f57869f fix(ll-box): support related root path
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk e21aff13ca fix(ll-box): correct argp
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk f02eba5848 refact(ll-box): remove annotations
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 953085409c refact(ll-box): create container item json file
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 249b134ed3 refact(ll-box): remove ll-dbus-proxy logic
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 5f975ce066 refact(ll-box): use argp
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk d223aea48c refact(ll-box): let loglevel effect syslog
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 8a36030481 refact(ll-box): set default log level to error
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 5b702272ae refact(ll-box): remove container option and loader
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk e47975ddd4 refact(ll-box): remove message reader
Signed-off-by: black-desk <me@black-desk.cn>
2024-09-26 19:07:00 +08:00
black-desk 29c57ce9da refactor: upgrade to c++ 17 and remove vendor libraries 2024-09-26 19:07:00 +08:00
black-desk 333b089f87
chore: release 1.3.4
Signed-off-by: black-desk <me@black-desk.cn>
2024-01-03 17:08:02 +08:00
linxin 3907448d51 fix: throw tl::bad_optional_access
throw tl::bad_optional_access in hook.env
2023-12-27 10:25:16 +08:00
black-desk 212a734812
build: remove /archlinux
Signed-off-by: black-desk <me@black-desk.cn>
2023-12-27 10:13:57 +08:00
black-desk 7f3de1c456
fix: correct syslog level
Signed-off-by: black-desk <me@black-desk.cn>
2023-10-30 16:13:25 +08:00
black-desk e28427c595
chore: always dump json
Signed-off-by: black-desk <me@black-desk.cn>
2023-10-30 16:13:24 +08:00
black-desk 705692d3aa
chore: update gitignore
Signed-off-by: black-desk <me@black-desk.cn>
2023-10-30 16:13:21 +08:00
lxy c633517c6c refact: fix a spelling mistake
Signed-off-by: lxy <89902721+hpdqddsy@users.noreply.github.com>
2023-09-15 18:09:47 +08:00
hpdqddsy 3058d4527e refact: refactor regex replacing logic
Signed-off-by: hpdqddsy <hpdqddsy@gmail.com>
2023-09-15 18:09:47 +08:00
Iceyer 5da2451f7f feat: skip parse {} avoid crash
- Skip get items in {} for optional key.

Signed-off-by: Iceyer <me@iceyer.net>
2023-09-12 11:05:32 +08:00
hudeng 0b1eea7b96 chore(CI): Update obs tag build workflow
use obs unstable package directly

log:
2023-09-12 11:05:09 +08:00
black-desk a277365099
chore: correct .reuse/dep5
Signed-off-by: black-desk <me@black-desk.cn>
2023-08-10 15:28:50 +08:00
deepin-admin-bot 839908d95d chore: Sync by 559e91167d
sync by github workflow

Log: none
Influence: none
2023-07-10 06:16:09 +00:00
deepin-admin-bot 7b0ce6f4a9 chore: Sync by 2e5e092ba3
sync by github workflow

Log: none
Influence: none
2023-05-10 02:26:01 +00:00
tsic404 17e22bdd85 chore: fix typo in PKGBUILD
log:
2023-04-06 12:35:49 +08:00
tsic404 71311c7874 chore(CI): fix typo
correct OBS workflows

log:
2023-03-28 14:46:24 +08:00
tsic404 f40c084f51 chore(CI): add PKGBUILD and obs workflows
log:
2023-03-14 08:01:50 +00:00
Chen Linxuan c6d5294f71 style: run clang-format
Just run clang-format on all .cpp/.h files.
2023-02-09 17:22:38 +08:00
Chen Linxuan 5001dec33e style: update clang-format
copy from dtksystemsettings
2023-02-09 17:22:38 +08:00
183 changed files with 27116 additions and 9745 deletions

View File

@ -1,139 +1,144 @@
# This is the clang-format style used by qt releated deepin projects.
#
# This file is modified from https://github.com/qt/qt5/blob/5b22f8ec2e4fbb58e362b709ee82f2dbd8afdfd3/_clang-format
# based on the rules from https://github.com/linuxdeepin/deepin-styleguide,
# https://wiki.qt.io/Qt_Coding_Style and https://wiki.qt.io/Coding_Conventions
#
# This is for clang-format >= 14.0.0
---
# Webkit style was loosely based on the Qt style
BasedOnStyle: WebKit
# 访问说明符的偏移(public private)
AccessModifierOffset: -4
# 括号之后,水平对齐参数: Align DontAlign AlwaysBreak。
AlignAfterOpenBracket: Align
#AlignConsecutiveBitFields: false
# 连续的赋值时,对齐所有的等号
AlignConsecutiveAssignments: false
# 连续声明时,对齐所有声明的变量名
AlignConsecutiveDeclarations: false
#AlignConsecutiveMacros: false
AlignEscapedNewlines: DontAlign
# 水平对齐二元和三元表达式的操作数
AlignOperands: true
# 对齐连续的尾随的注释
AlignTrailingComments: false
#AllowAllArgumentsOnNextLine: false
#AllowAllConstructorInitializersOnNextLine: false
# 允许函数声明的所有参数在放在下一行
AllowAllParametersOfDeclarationOnNextLine: false
# 允许短的块放在同一行
AllowShortBlocksOnASingleLine: true
# 允许短的case标签放在同一行
AllowShortCaseLabelsOnASingleLine: false
#AllowShortEnumsOnASingleLine: false
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
AllowShortFunctionsOnASingleLine: InlineOnly
# 是否允许短if单行 If true, if (a) return; 可以放到同一行
AllowShortIfStatementsOnASingleLine: false
#AllowShortLambdasOnASingleLine: Inline
# 允许短的循环保持在同一行
AllowShortLoopsOnASingleLine: false
# 总是在定义返回类型后换行(deprecated)
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
# deepin project might not use same standard.
Standard: Auto
# Column width is limited to 100 in accordance with Qt Coding Style.
# https://wiki.qt.io/Qt_Coding_Style
# Note that this may be changed at some point in the future.
ColumnLimit: 100
# How much weight do extra characters after the line length limit have.
# PenaltyExcessCharacter: 4
# Disable reflow of some specific comments
# qdoc comments: indentation rules are different.
# Translation comments and SPDX license identifiers are also excluded.
CommentPragmas: "^!|^:|^ SPDX-License-Identifier:"
PointerAlignment: Right
# We use template< without space.
SpaceAfterTemplateKeyword: false
# We want to break before the operators, but not before a '='.
BreakBeforeBinaryOperators: NonAssignment
# Braces are usually attached, but not after functions or class declarations.
BreakBeforeBraces: Custom
BraceWrapping:
# AfterCaseLabel: false
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterStruct: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
# BeforeLambdaBody: false
# BeforeWhile: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
IndentBraces: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
#在构造函数初始化时按逗号断行,并以冒号对齐
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: false
# 每行字符的限制0表示没有限制
ColumnLimit: 120
# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
# CommentPragmas: '^ IWYU pragma:'
# 语言: None Cpp Java Objc Protp
CompactNamespaces: false
Cpp11BracedListStyle: true
#DeriveLineEnding: \n
DerivePointerAlignment: false
FixNamespaceComments: true
ForEachMacros: [ "foreach", "Q_FOREACH" ]
# Indent initializers by 4 spaces
ConstructorInitializerIndentWidth: 4
# Indent width for line continuations.
ContinuationIndentWidth: 8
# No indentation for namespaces.
NamespaceIndentation: None
# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125
IndentPPDirectives: AfterHash
# We do not indent preprocessor directives
PPIndentWidth: 0
# Horizontally align arguments after an open bracket.
# The coding style does not specify the following, but this is what gives
# results closest to the existing code.
AlignAfterOpenBracket: true
AlwaysBreakTemplateDeclarations: true
# Ideally we should also allow less short function in a single line, but
# clang-format does not handle that.
AllowShortFunctionsOnASingleLine: Inline
# As clang-format 13 can regroup includes we enable this feature.
# basically according to https://wiki.qt.io/Coding_Conventions#Including_headers
# and https://github.com/linuxdeepin/deepin-styleguide
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"'
# gtest/gmock's h files
- Regex: '^<g(test|mock)/'
Priority: 1
- Regex: '^["<]D'
CaseSensitive: true
# Your project's h files.
- Regex: '^"'
Priority: 2
- Regex: '^<Q'
Priority: 3
- Regex: '^<'
# DTK libraries' h files
- Regex: '^<D[A-Z]'
Priority: 4
- Regex: '\.h>$'
CaseSensitive: true
# QT libraries' h files
- Regex: '^<(Q[A-Z]|Qt)'
Priority: 5
- Regex: '^"moc_'
Priority: 99
- Regex: '\.moc"'
Priority: 99
CaseSensitive: true
# C++ system headers (as of C++23).
- Regex: '^<(algorithm|any|array|atomic|barrier|bit|bitset|cassert|ccomplex|cctype|cerrno|cfenv|cfloat|charconv|chrono|cinttypes|ciso646|climits|clocale|cmath|codecvt|compare|complex|concepts|condition_variable|coroutine|csetjmp|csignal|cstdalign|cstdarg|cstdbool|cstddef|cstdint|cstdio|cstdlib|cstring|ctgmath|ctime|cuchar|cwchar|cwctype|deque|exception|execution|expected|filesystem|flat_map|flat_set|format|forward_list|fstream|functional|future|generator|initializer_list|iomanip|ios|iosfwd|iostream|istream|iterator|latch|limits|list|locale|map|mdspan|memory|memory_resource|mutex|new|numbers|numeric|optional|ostream|print|queue|random|ranges|ratio|regex|scoped_allocator|semaphore|set|shared_mutex|source_location|span|spanstream|sstream|stack|stacktrace|stdexcept|stdfloat|stop_token|streambuf|string|string_view|strstream|syncstream|system_error|thread|tuple|type_traits|typeindex|typeinfo|unordered_map|unordered_set|utility|valarray|variant|vector|version)>$'
Priority: 6
CaseSensitive: true
# C system headers.
- Regex: '^<(aio|arpa/inet|assert|complex|cpio|ctype|curses|dirent|dlfcn|errno|fcntl|fenv|float|fmtmsg|fnmatch|ftw|glob|grp|iconv|inttypes|iso646|langinfo|libgen|limits|locale|math|monetary|mqueue|ndbm|netdb|net/if|netinet/in|netinet/tcp|nl_types|poll|pthread|pwd|regex|sched|search|semaphore|setjmp|signal|spawn|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|strings|stropts|sys/ipc|syslog|sys/mman|sys/msg|sys/resource|sys/select|sys/sem|sys/shm|sys/socket|sys/stat|sys/statvfs|sys/time|sys/times|sys/types|sys/uio|sys/un|sys/utsname|sys/wait|tar|term|termios|tgmath|threads|time|trace|uchar|ulimit|uncntrl|unistd|utime|utmpx|wchar|wctype|wordexp)\.h>$'
Priority: 7
CaseSensitive: true
# other libraries' h files.
- Regex: '^<'
Priority: 3
IncludeIsMainRegex: '((T|t)est)?$'
SortIncludes: true
# 缩进case 标签
IndentCaseLabels: false
#IndentExternBlock: NoIndent
#IndentCaseBlocks: false
#IndentGotoLabels: false
IndentPPDirectives: None
#缩进宽度
IndentWidth: 4
IndentWrappedFunctionNames: false
#InsertTrailingCommas: None
#在block从空行开始
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
# 连续的空行保留几行
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#指针的*的挨着哪边
PointerAlignment: Right
# 允许排序#include, 造成编译错误
SortIncludes: false
SortUsingDeclarations: false
#括号后添加空格
SpaceAfterCStyleCast: false
#SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
#等号两边的空格
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
#SpaceBeforeSquareBrackets: false
#SpaceInEmptyBlock: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
#SpacesInConditionalStatement: false
# 容器类的空格 例如 OC的字典
SpacesInContainerLiterals: false
# 小括号两边添加空格
SpacesInParentheses: false
# 中括号两边空格 []
SpacesInSquareBrackets: false
#StatementMacros: ["Q_UNUSED"]
#tab键盘的宽度
TabWidth: 4
UseTab: Never
# macros for which the opening brace stays attached.
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
# Break constructor initializers before the colon and after the commas.
BreakConstructorInitializers: BeforeComma
# Add "// namespace <namespace>" comments on closing brace for a namespace
# Ignored for namespaces that qualify as a short namespace,
# see 'ShortNamespaceLines'
FixNamespaceComments: true
# Definition of how short a short namespace is, default 1
ShortNamespaceLines: 1
# When escaping newlines in a macro attach the '\' as far left as possible, e.g.
##define a \
# something; \
# other; \
# thelastlineislong;
AlignEscapedNewlines: Left
# Avoids the addition of a space between an identifier and the
# initializer list in list-initialization.
SpaceBeforeCpp11BracedList: false
AllowAllArgumentsOnNextLine: false
AllowShortLambdasOnASingleLine: Empty
AlignTrailingComments: true
BinPackArguments: false
BinPackParameters: false
PackConstructorInitializers: Never
PenaltyReturnTypeOnItsOwnLine: 100
SeparateDefinitionBlocks: Always
WhitespaceSensitiveMacros:
- Q_PROPERTY
- Q_INTERFACES

107
.cmake-format.py Normal file
View File

@ -0,0 +1,107 @@
# SPDX-FileCopyrightText: None
#
# SPDX-License-Identifier: CC0-1.0
with section("parse"):
additional_commands = {
"cpmaddpackage": {
"kwargs": {
"DOWNLOAD_COMMAND": 1,
"DOWNLOAD_NAME": 1,
"DOWNLOAD_NO_EXTRACT": 1,
"DOWNLOAD_ONLY": 1,
"EXCLUDE_FROM_ALL": 1,
"FIND_PACKAGE_ARGUMENTS": 1,
"FORCE": 1,
"GITHUB_REPOSITORY": 1,
"GITLAB_REPOSITORY": 1,
"GIT_REPOSITORY": 1,
"GIT_SHALLOW": 1,
"GIT_TAG": 1,
"HTTP_PASSWORD": 1,
"HTTP_USERNAME": 1,
"NAME": 1,
"NO_CACHE": 1,
"OPTIONS": "+",
"SOURCE_DIR": 1,
"SOURCE_SUBDIR": 1,
"SVN_REPOSITORY": 1,
"SVN_REVISION": 1,
"URL": 1,
"URL_HASH": 1,
"URL_MD5": 1,
"VERSION": 1,
},
"pargs": {"flags": [], "nargs": "*"},
"spelling": "CPMAddPackage",
},
"cpmdeclarepackage": {
"kwargs": {
"DOWNLOAD_COMMAND": 1,
"DOWNLOAD_NAME": 1,
"DOWNLOAD_NO_EXTRACT": 1,
"DOWNLOAD_ONLY": 1,
"EXCLUDE_FROM_ALL": 1,
"FIND_PACKAGE_ARGUMENTS": 1,
"FORCE": 1,
"GITHUB_REPOSITORY": 1,
"GITLAB_REPOSITORY": 1,
"GIT_REPOSITORY": 1,
"GIT_SHALLOW": 1,
"GIT_TAG": 1,
"HTTP_PASSWORD": 1,
"HTTP_USERNAME": 1,
"NAME": 1,
"NO_CACHE": 1,
"OPTIONS": "+",
"SOURCE_DIR": 1,
"SOURCE_SUBDIR": 1,
"SVN_REPOSITORY": 1,
"SVN_REVISION": 1,
"URL": 1,
"URL_HASH": 1,
"URL_MD5": 1,
"VERSION": 1,
},
"pargs": {"flags": [], "nargs": "*"},
"spelling": "CPMDeclarePackage",
},
"cpmfindpackage": {
"kwargs": {
"DOWNLOAD_COMMAND": 1,
"DOWNLOAD_NAME": 1,
"DOWNLOAD_NO_EXTRACT": 1,
"DOWNLOAD_ONLY": 1,
"EXCLUDE_FROM_ALL": 1,
"FIND_PACKAGE_ARGUMENTS": 1,
"FORCE": 1,
"GITHUB_REPOSITORY": 1,
"GITLAB_REPOSITORY": 1,
"GIT_REPOSITORY": 1,
"GIT_SHALLOW": 1,
"GIT_TAG": 1,
"HTTP_PASSWORD": 1,
"HTTP_USERNAME": 1,
"NAME": 1,
"NO_CACHE": 1,
"OPTIONS": "+",
"SOURCE_DIR": 1,
"SOURCE_SUBDIR": 1,
"SVN_REPOSITORY": 1,
"SVN_REVISION": 1,
"URL": 1,
"URL_HASH": 1,
"URL_MD5": 1,
"VERSION": 1,
},
"pargs": {"flags": [], "nargs": "*"},
"spelling": "CPMFindPackage",
},
"cpmgetpackageversion": {"pargs": 2, "spelling": "CPMGetPackageVersion"},
"cpmregisterpackage": {"pargs": 1, "spelling": "CPMRegisterPackage"},
"cpmusepackagelock": {"pargs": 1, "spelling": "CPMUsePackageLock"},
}
with section("format"):
# How wide to allow formatted cmake files
line_width = 80

76
.deepsource.toml Normal file
View File

@ -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",
]

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: None
#
# SPDX-License-Identifier: CC0-1.0
[*.json]
indent_style = tab
indent_size = 2
trim_trailing_whitespace = true
charset = utf-8

View File

@ -8,10 +8,8 @@ concurrency:
jobs:
backup-to-gitlabwh:
uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master
secrets:
BRIDGETOKEN: ${{ secrets.BRIDGETOKEN }}
secrets: inherit
backup-to-gitee:
uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master
secrets:
GITEE_SYNC_TOKEN: ${{ secrets.GITEE_SYNC_TOKEN }}
secrets: inherit

View File

@ -1,16 +0,0 @@
name: auto tag
on:
pull_request_target:
types: [opened, synchronize, closed]
paths:
- "debian/changelog"
concurrency:
group: ${{ github.workflow }}-pull/${{ github.event.number }}
cancel-in-progress: true
jobs:
auto_tag:
uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master
secrets: inherit

View File

@ -1,17 +0,0 @@
name: Call build-deb
on:
pull_request_target:
paths-ignore:
- ".github/workflows/**"
types: [ opened, closed, synchronize ]
concurrency:
group: ${{ github.workflow }}-pull/${{ github.event.number }}
cancel-in-progress: true
jobs:
check_job:
if: github.event.action != 'closed' || github.event.pull_request.merged
uses: linuxdeepin/.github/.github/workflows/build-deb.yml@master
secrets:
BridgeToken: ${{ secrets.BridgeToken }}

View File

@ -10,8 +10,4 @@ on:
jobs:
check_job:
uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master
secrets:
BUILD_GPG_PRIVATE_KEY: ${{ secrets.BUILD_GPG_PRIVATE_KEY }}
BUILD_SSH_PRIVATE_KEY: ${{ secrets.BUILD_SSH_PRIVATE_KEY }}
WEBDAV_PASSWD: ${{ secrets.WEBDAV_PASSWD }}
WEBDAV_USER: ${{ secrets.WEBDAV_USER }}
secrets: inherit

View File

@ -6,5 +6,4 @@ on:
jobs:
chatopt:
uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master
secrets:
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
secrets: inherit

View File

@ -1,16 +0,0 @@
name: Call CLA check
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize]
concurrency:
group: ${{ github.workflow }}-pull/${{ github.event.number }}
cancel-in-progress: true
jobs:
clacheck:
uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master
secrets:
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}

View File

@ -1,13 +0,0 @@
name: tag build
on:
push:
tags: "*"
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
uses: linuxdeepin/.github/.github/workflows/tag-build.yml@master
secrets: inherit

View File

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- run: export
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false

10
.gitignore vendored
View File

@ -1,2 +1,10 @@
.idea/
/build
/build*
/obj-x86_64-linux-gnu
/debian/.debhelper
/debian/linglong-box
/debian/debhelper-build-stamp
/debian/linglong-box.debhelper.log
/debian/files
/debian/linglong-box.substvars
.vscode/

42
.obs/workflows.yml Normal file
View File

@ -0,0 +1,42 @@
test_build:
steps:
- link_package:
source_project: deepin:Develop:dde
source_package: %{SCM_REPOSITORY_NAME}
target_project: deepin:CI
- configure_repositories:
project: deepin:CI
repositories:
- name: deepin_develop
paths:
- target_project: deepin:CI
target_repository: deepin_develop
architectures:
- x86_64
- aarch64
- name: debian
paths:
- target_project: deepin:CI
target_repository: debian_sid
architectures:
- x86_64
filters:
event: pull_request
tag_build:
steps:
- trigger_services:
project: deepin:Unstable:dde
package: %{SCM_REPOSITORY_NAME}
filters:
event: tag_push
commit_build:
steps:
- trigger_services:
project: deepin:Develop:dde
package: %{SCM_REPOSITORY_NAME}
filters:
event: push

View File

@ -1,59 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: linglong-box
Upstream-Contact: UnionTech Software Technology Co., Ltd. <>
Source: https://github.com/linuxdeepin/linglong-box
# ci
Files: .github/* .gitlab-ci.yml test/data/demo/*.json test/data/demo/*.yaml test/data/demo/*.yml
Copyright: None
License: CC0-1.0
# Packaging configuration
Files: rpm/* debian/* archlinux/*
Copyright: None
License: CC0-1.0
# README & DOC
Files: README.md README.zh_CN.md INSTALL.md CHANGELOG.md doc/*.md src/*.md docs/*.md
Copyright: UnionTech Software Technology Co., Ltd.
License: CC-BY-4.0
# translation
Files: translations/* plugins/*.ts
Copyright: UnionTech Software Technology Co., Ltd.
License: LGPL-3.0-or-later
# png svg
Files: plugins/*.png plugins/*.svg frame/*.svg tests/*.png tests/*.svg
Copyright: UnionTech Software Technology Co., Ltd.
License: LGPL-3.0-or-later
# qrc
Files: plugins/*.qrc frame/*.qrc tests/*.qrc
Copyright: None
License: CC0-1.0
# Project file
Files: CMakeLists.txt .gitignore src/CMakeLists.txt test/CMakeLists.txt test/protype/CMakeLists.txt 3party/nlohmann/json.hpp 3party/optional/optional.hpp
Copyright: None
License: CC0-1.0
# xml toml json policy yaml
Files: gschema/*.xml plugins/*.xml frame/*.xml .clog.toml plugins/*.json configs/*.json .packit.yaml .tx/config test/data/demo/*.json src/service/resource/*.json
Copyright: None
License: CC0-1.0
# in
Files: .clang-format .cmake-format.py
Copyright: None
License: CC0-1.0
# sh
Files: gen_report.sh lupdate.sh plugins/dcc-dock-plugin/translate_generation.sh tests/test-recoverage.sh translate_generation.sh scripts/*
Copyright: None
License: CC0-1.0
# xml2cpp
Files: frame/dbus/* plugins/disk-mount/dbus/dbusdiskmount.* plugins/power/dbus/dbuspower.* plugins/shutdown/dbus/dbuspowermanager.* plugins/tray/dbus/dbustraymanager.* src/module/dbus_ipc/* src/module/dbus_ipc/*
Copyright: The Qt Company Ltd.
License: CC0-1.0

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,520 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.11.4) # for RHEL 8
project(linglong-box)
project(
linyaps-box
VERSION 2.1.3
DESCRIPTION "A simple OCI runtime for desktop applications"
HOMEPAGE_URL "https://github.com/OpenAtom-Linyaps/linyaps-box"
LANGUAGES CXX)
#
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# ==============================================================================
# Utilities
# ==============================================================================
set(VERSION "1.0")
# NOTE: Modified from https://www.scivision.dev/cmake-project-is-top-level/
if(CMAKE_VERSION VERSION_LESS 3.21)
get_property(
not_top
DIRECTORY
PROPERTY PARENT_DIRECTORY)
if(NOT not_top)
set(linyaps-box_IS_TOP_LEVEL true)
else()
set(linyaps-box_IS_TOP_LEVEL false)
endif()
endif()
option(BUILD_STATIC "Build the static binary" ON)
# ==============================================================================
# Build options
# ==============================================================================
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
if (NOT CMAKE_DEBUG_ASAN)
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -Wall -g -ggdb3")
else ()
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fsanitize=undefined,address -Wall -g -ggdb3")
set(BUILD_STATIC OFF)
endif ()
endif ()
option(linyaps-box_STATIC "Build linyaps-box as statically-linked binary." OFF)
option(linyaps-box_ENABLE_SECCOMP "Build linyaps-box with seccomp support." OFF)
option(linyaps-box_ENABLE_CAP "Build linyaps-box with capability support" ON)
option(linyaps-box_ENABLE_UNIT_TESTS "Enable unit tests."
${linyaps-box_IS_TOP_LEVEL})
option(linyaps-box_ENABLE_SMOKE_TESTS "Enable smoke tests." OFF)
option(linyaps-box_MAKE_RELEASE "Make release build." OFF)
if(linyaps-box_ENABLE_SMOKE_TESTS OR linyaps-box_ENABLE_UNIT_TESTS)
set(linyaps-box_ENABLE_TESTING ON)
endif()
option(linyaps-box_ENABLE_COVERAGE "Enable coverage." OFF)
if(linyaps-box_ENABLE_COVERAGE AND NOT linyaps-box_ENABLE_TESTING)
message(
FATAL_ERROR
"linyaps-box_ENABLE_COVERAGE requires linyaps-box_ENABLE_UNIT_TESTS or linyaps-box_ENABLE_SMOKE_TESTS."
)
endif()
option(linyaps-box_ENABLE_CPACK "Enable CPack." OFF)
if(linyaps-box_ENABLE_CPACK)
set(linyaps-box_CPACK_PACKAGING_INSTALL_PREFIX
"/opt/org.openatom.linyaps-box"
CACHE STRING "Install prefix for package generated by CPack.")
endif()
set(linyaps-box_CLONE_CHILD_STACK_SIZE
"(1U << 20)"
CACHE
STRING
"DO NOT MODIFY THIS SETTINGS UNLESS YOU KNOW WHAT YOU ARE DOING. Size of child stack in bytes when using clone(2) to create container."
)
set(linyaps-box_STACK_GROWTH_DOWN
true
CACHE
BOOL
"DO NOT MODIFY THIS SETTINGS UNLESS YOU KNOW WHAT YOU ARE DOING. If stacks grow upward on your processor, set this to false."
)
set(linyaps-box_DEFAULT_LOG_LEVEL
LOG_WARNING
CACHE
STRING
"The default syslog priority. This is used to filter log messages at runtime time."
)
set(linyaps-box_ACTIVE_LOG_LEVEL
LOG_WARNING
CACHE
STRING
"The active syslog priority. This is used to filter log messages at compile time."
)
set(linyaps-box_ENABLE_CPM
OFF
CACHE BOOL "enable CPM")
if(CMAKE_VERSION VERSION_LESS "3.14")
set(linyaps-box_ENABLE_CPM OFF)
message(
STATUS "cmake version ${CMAKE_VERSION} not compatible with CPM.cmake.")
endif()
# if just want to try local packages at first set CPM_USE_LOCAL_PACKAGES ON
set(linyaps-box_CPM_LOCAL_PACKAGES_ONLY
OFF
CACHE BOOL "use local packages only")
# ==============================================================================
set(linyaps-box_LIBRARY linyaps-box)
set(linyaps-box_LIBRARY_SOURCE
# find -regex '\./src/.+\.[ch]\(pp\)?' -type f -printf '%P\n'| sort
src/linyaps_box/app.cpp
src/linyaps_box/app.h
src/linyaps_box/cgroup.h
src/linyaps_box/cgroup_manager.cpp
src/linyaps_box/cgroup_manager.h
src/linyaps_box/command/exec.cpp
src/linyaps_box/command/exec.h
src/linyaps_box/command/kill.cpp
src/linyaps_box/command/kill.h
src/linyaps_box/command/list.cpp
src/linyaps_box/command/list.h
src/linyaps_box/command/options.cpp
src/linyaps_box/command/options.h
src/linyaps_box/command/run.cpp
src/linyaps_box/command/run.h
src/linyaps_box/config.cpp
src/linyaps_box/config.h
src/linyaps_box/container.cpp
src/linyaps_box/container.h
src/linyaps_box/container_ref.cpp
src/linyaps_box/container_ref.h
src/linyaps_box/container_status.cpp
src/linyaps_box/container_status.h
src/linyaps_box/impl/disabled_cgroup_manager.cpp
src/linyaps_box/impl/disabled_cgroup_manager.h
src/linyaps_box/impl/json_printer.cpp
src/linyaps_box/impl/json_printer.h
src/linyaps_box/impl/status_directory.cpp
src/linyaps_box/impl/status_directory.h
src/linyaps_box/impl/table_printer.cpp
src/linyaps_box/impl/table_printer.h
src/linyaps_box/interface.cpp
src/linyaps_box/interface.h
src/linyaps_box/printer.cpp
src/linyaps_box/printer.h
src/linyaps_box/runtime.cpp
src/linyaps_box/runtime.h
src/linyaps_box/status_directory.cpp
src/linyaps_box/status_directory.h
src/linyaps_box/utils/atomic_write.cpp
src/linyaps_box/utils/atomic_write.h
src/linyaps_box/utils/cgroups.cpp
src/linyaps_box/utils/cgroups.h
src/linyaps_box/utils/close_range.cpp
src/linyaps_box/utils/close_range.h
src/linyaps_box/utils/defer.h
src/linyaps_box/utils/file_describer.cpp
src/linyaps_box/utils/file_describer.h
src/linyaps_box/utils/fstat.cpp
src/linyaps_box/utils/fstat.h
src/linyaps_box/utils/inspect.cpp
src/linyaps_box/utils/inspect.h
src/linyaps_box/utils/log.cpp
src/linyaps_box/utils/log.h
src/linyaps_box/utils/mkdir.cpp
src/linyaps_box/utils/mkdir.h
src/linyaps_box/utils/mknod.cpp
src/linyaps_box/utils/mknod.h
src/linyaps_box/utils/open_file.cpp
src/linyaps_box/utils/open_file.h
src/linyaps_box/utils/platform.cpp
src/linyaps_box/utils/platform.h
src/linyaps_box/utils/semver.cpp
src/linyaps_box/utils/semver.h
src/linyaps_box/utils/socketpair.cpp
src/linyaps_box/utils/socketpair.h
src/linyaps_box/utils/symlink.cpp
src/linyaps_box/utils/symlink.h
src/linyaps_box/utils/touch.cpp
src/linyaps_box/utils/touch.h)
set(LINYAPS_BOX_VERSION ${PROJECT_VERSION})
if(NOT linyaps-box_MAKE_RELEASE)
message(STATUS "make dev build")
execute_process(
COMMAND git rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(LINYAPS_BOX_VERSION "${LINYAPS_BOX_VERSION}-dev-${GIT_COMMIT_HASH}")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/linyaps_box/version.h.in
${CMAKE_CURRENT_BINARY_DIR}/src/linyaps_box/version.h @ONLY)
# for gcc 8
set(linyaps-box_LIBRARY_LINK_LIBRARIES "stdc++fs")
set(linyaps-box_LIBRARY_INCLUDE_DIRS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_BINARY_DIR}/src")
if(linyaps-box_STATIC)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif()
find_package(PkgConfig REQUIRED)
if(linyaps-box_ENABLE_SECCOMP)
pkg_check_modules(libseccomp REQUIRED IMPORTED_TARGET libseccomp>=2.3.3)
list(APPEND linyaps-box_LIBRARY_LINK_LIBRARIES PUBLIC PkgConfig::libseccomp)
endif()
if(linyaps-box_ENABLE_CAP)
pkg_check_modules(libcap REQUIRED IMPORTED_TARGET libcap>=2.25)
list(APPEND linyaps-box_LIBRARY_LINK_LIBRARIES PUBLIC PkgConfig::libcap)
endif()
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
GITHUB_REPOSITORY nlohmann/json
GIT_TAG v3.12.0
EXCLUDE_FROM_ALL ON
OPTIONS "JSON_BuildTests OFF")
CPMFindPackage(
NAME CLI11
VERSION 2.5.0
GITHUB_REPOSITORY CLIUtils/CLI11
GIT_TAG v2.5.0
EXCLUDE_FROM_ALL ON
OPTIONS "CLI11_BUILD_TESTS OFF")
endif()
find_package(nlohmann_json 3.11.3 QUIET)
if(NOT nlohmann_json_FOUND)
add_subdirectory(external/nlohmann_json)
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake.external/nlohmann_json")
find_package(nlohmann_json 3.11.3 REQUIRED)
message(STATUS "use vendor nlohmann_json ${nlohmann_json_VERSION}")
endif()
list(APPEND linyaps-box_LIBRARY_LINK_LIBRARIES PUBLIC
nlohmann_json::nlohmann_json)
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.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}" STATIC ${linyaps-box_LIBRARY_SOURCE})
target_include_directories("${linyaps-box_LIBRARY}"
${linyaps-box_LIBRARY_INCLUDE_DIRS})
target_link_libraries("${linyaps-box_LIBRARY}"
PRIVATE ${linyaps-box_LIBRARY_LINK_LIBRARIES})
target_compile_features("${linyaps-box_LIBRARY}" PUBLIC cxx_std_17)
set_property(TARGET "${linyaps-box_LIBRARY}" PROPERTY CXX_STANDARD 17)
set_property(TARGET "${linyaps-box_LIBRARY}" PROPERTY CXX_EXTENSIONS OFF)
set_property(TARGET "${linyaps-box_LIBRARY}" PROPERTY CXX_STANDARD_REQUIRED ON)
include(CheckIncludeFileCXX)
check_include_file_cxx("linux/openat2.h" LINYAPS_BOX_HAVE_OPENAT2_H)
if(${LINYAPS_BOX_HAVE_OPENAT2_H})
target_compile_definitions("${linyaps-box_LIBRARY}"
PRIVATE "LINYAPS_BOX_HAVE_OPENAT2_H")
endif()
target_compile_definitions(
"${linyaps-box_LIBRARY}"
PRIVATE
"LINYAPS_BOX_CLONE_CHILD_STACK_SIZE=${linyaps-box_CLONE_CHILD_STACK_SIZE}"
PRIVATE "LINYAPS_BOX_STACK_GROWTH_DOWN=${linyaps-box_STACK_GROWTH_DOWN}"
PRIVATE "LINYAPS_BOX_DEFAULT_LOG_LEVEL=${linyaps-box_DEFAULT_LOG_LEVEL}"
PRIVATE "LINYAPS_BOX_ACTIVE_LOG_LEVEL=${linyaps-box_ACTIVE_LOG_LEVEL}")
target_compile_options("${linyaps-box_LIBRARY}"
PRIVATE -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
if(linyaps-box_STATIC)
target_compile_definitions("${linyaps-box_LIBRARY}"
PUBLIC "LINYAPS_BOX_STATIC_LINK")
endif()
if(linyaps-box_ENABLE_SECCOMP)
target_compile_definitions("${linyaps-box_LIBRARY}"
PUBLIC LINYAPS_BOX_ENABLE_SECCOMP)
endif()
if(linyaps-box_ENABLE_CAP)
target_compile_definitions("${linyaps-box_LIBRARY}"
PUBLIC LINYAPS_BOX_ENABLE_CAP)
endif()
if(linyaps-box_ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options("${linyaps-box_LIBRARY}"
PRIVATE -fprofile-instr-generate -fcoverage-mapping)
target_link_options("${linyaps-box_LIBRARY}" PUBLIC
-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options("${linyaps-box_LIBRARY}" PRIVATE --coverage)
target_link_options("${linyaps-box_LIBRARY}" PUBLIC --coverage)
else()
message(
FATAL_ERROR "Coverage is not supported for ${CMAKE_CXX_COMPILER_ID}.")
endif()
endif()
# ==============================================================================
set(linyaps-box_APP ll-box)
set(linyaps-box_APP_SOURCE "./app/${linyaps-box_APP}/src/main.cpp")
set(linyaps-box_APP_LINK_LIBRARIES PRIVATE "${linyaps-box_LIBRARY}")
set(linyaps-box_APP_SOURCE_INCLUDE_DIRS
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/app/${linyaps-box_APP}/src")
add_executable("${linyaps-box_APP}" ${linyaps-box_APP_SOURCE})
target_include_directories("${linyaps-box_APP}"
${linyaps-box_APP_SOURCE_INCLUDE_DIRS})
target_link_libraries("${linyaps-box_APP}" ${linyaps-box_APP_LINK_LIBRARIES})
if(linyaps-box_STATIC)
target_link_options("${linyaps-box_APP}" PRIVATE -static -static-libgcc
-static-libstdc++)
endif()
target_compile_features("${linyaps-box_APP}" PRIVATE cxx_std_17)
set_property(TARGET "${linyaps-box_APP}" PROPERTY CXX_STANDARD 17)
set_property(TARGET "${linyaps-box_APP}" PROPERTY CXX_EXTENSIONS OFF)
set_property(TARGET "${linyaps-box_APP}" PROPERTY CXX_STANDARD_REQUIRED ON)
target_compile_definitions(
"${linyaps-box_APP}"
PRIVATE "LINYAPS_BOX_DEFAULT_LOG_LEVEL=${linyaps-box_DEFAULT_LOG_LEVEL}"
PRIVATE "LINYAPS_BOX_ACTIVE_LOG_LEVEL=${linyaps-box_AVTIVE_LOG_LEVEL}")
target_compile_options("${linyaps-box_APP}"
PRIVATE -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
# ==============================================================================
include(GNUInstallDirs)
include_directories(${linglong-box_SOURCE_DIR})
include_directories(${linglong-box_SOURCE_DIR}/src)
# parameter TYPE was added in CMake 3.14
if(CMAKE_VERSION VERSION_LESS "3.14")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${linyaps-box_APP}"
DESTINATION "${CMAKE_INSTALL_BINDIR}")
else()
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${linyaps-box_APP}" TYPE BIN)
endif()
find_package(PkgConfig)
if(linyaps-box_ENABLE_CPACK)
set(CPACK_PACKAGING_INSTALL_PREFIX
"${linyaps-box_CPACK_PACKAGING_INSTALL_PREFIX}")
set(CPACK_PACKAGE_CONTACT "chenlinxuan@uniontech.com")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_RPM_PACKAGE_AUTOREQ ON)
include(CPack)
endif()
add_subdirectory(src)
# ==============================================================================
add_subdirectory(test)
if(NOT linyaps-box_ENABLE_TESTING)
return()
endif()
enable_testing()
include(GoogleTest)
set(linyaps-box_UNIT_TESTS ll-box-ut)
set(linyaps-box_UNIT_TESTS_SOURCE ./tests/ll-box-ut/src/test.cpp)
set(linyaps-box_UNIT_TESTS_LINK_LIBRARIES PRIVATE "${linyaps-box_LIBRARY}")
set(linyaps-box_UNIT_TESTS_SOURCE_INCLUDE_DIRS
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tests/ll-box-ut/src")
find_package(GTest REQUIRED)
if(CMAKE_VERSION VERSION_LESS 3.20)
add_library(GTest::gtest_main INTERFACE IMPORTED)
target_link_libraries(GTest::gtest_main INTERFACE GTest::Main)
endif()
list(APPEND linyaps-box_UNIT_TESTS_LINK_LIBRARIES PRIVATE GTest::gtest_main)
add_executable("${linyaps-box_UNIT_TESTS}" ${linyaps-box_UNIT_TESTS_SOURCE})
target_include_directories("${linyaps-box_UNIT_TESTS}"
${linyaps-box_UNIT_TESTS_SOURCE_INCLUDE_DIRS})
target_link_libraries("${linyaps-box_UNIT_TESTS}"
${linyaps-box_UNIT_TESTS_LINK_LIBRARIES})
target_compile_features("${linyaps-box_UNIT_TESTS}" PRIVATE cxx_std_17)
set_property(TARGET "${linyaps-box_UNIT_TESTS}" PROPERTY CXX_STANDARD 17)
set_property(TARGET "${linyaps-box_UNIT_TESTS}" PROPERTY CXX_EXTENSIONS OFF)
set_property(TARGET "${linyaps-box_UNIT_TESTS}" PROPERTY CXX_STANDARD_REQUIRED
ON)
target_compile_options("${linyaps-box_UNIT_TESTS}"
PRIVATE -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
set(GTEST_DISCOVER_TESTS_ARGS "${linyaps-box_UNIT_TESTS}" WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}/tests/ll-box-ut")
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND GTEST_DISCOVER_TESTS_ARGS PROPERTIES ENVIRONMENT
"LLVM_PROFILE_FILE=/dev/null")
endif()
gtest_discover_tests(${GTEST_DISCOVER_TESTS_ARGS})
function(setup_linyaps_box_smoke_tests)
if(NOT linyaps-box_ENABLE_SMOKE_TESTS)
return()
endif()
set(linyaps-box_SMOKE_TESTS
./tests/ll-box-st/01-run-whoami.json
./tests/ll-box-st/02-check-procfs.json
./tests/ll-box-st/03-check-mounts.json
./tests/ll-box-st/04-check-noNewPrivs.json
./tests/ll-box-st/05-check-env.json
./tests/ll-box-st/06-check-cwd.json
./tests/ll-box-st/07-check-capability.json
./tests/ll-box-st/08-check-umask.json
./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/13-pid-extension.json)
foreach(test ${linyaps-box_SMOKE_TESTS})
add_test(
NAME "${test}"
COMMAND
"${CMAKE_CURRENT_SOURCE_DIR}/tests/ll-box-st/ll-box-st"
"${CMAKE_CURRENT_BINARY_DIR}/${linyaps-box_APP}"
"${CMAKE_CURRENT_BINARY_DIR}/st-data"
"${CMAKE_CURRENT_SOURCE_DIR}/${test}")
list(APPEND linyaps-box_TESTS "${test}")
set_tests_properties(
"${test}"
PROPERTIES
ENVIRONMENT
"LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_LIST_DIR}/tests/ll-box-st/glibc_leaks.txt"
)
endforeach()
endfunction()
setup_linyaps_box_smoke_tests()
function(setup_linyaps_box_coverage)
if(NOT linyaps-box_ENABLE_COVERAGE)
return()
endif()
set(COVERAGE_INFO "coverage.info")
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set_tests_properties(
${linyaps-box_TESTS}
PROPERTIES ENVIRONMENT
"LLVM_PROFILE_FILE=${CMAKE_CURRENT_BINARY_DIR}/default.profraw"
)
find_program(LLVM_PROFDATA llvm-profdata REQUIRED)
add_custom_command(
OUTPUT default.profdata
DEPENDS "${linyaps-box_APP}" test
COMMAND "${LLVM_PROFDATA}" merge -sparse default.profraw -o
default.profdata)
find_program(LLVM_COV llvm-cov REQUIRED)
add_custom_command(
OUTPUT coverage.info
DEPENDS default.profdata
COMMAND "${LLVM_COV}" show -instr-profile=default.profdata
-object="$<TARGET_FILE_NAME:${linyaps-box_APP}>" > coverage.info)
add_custom_command(
OUTPUT coverage.tar.gz
DEPENDS default.profdata
COMMAND rm -rf coverage
COMMAND
"${LLVM_COV}" show -instr-profile=default.profdata -format=html
-object="$<TARGET_FILE_NAME:${linyaps-box_APP}>" -output-dir=coverage
COMMAND tar -czf coverage-html.tar.gz coverage)
add_custom_target(coverage DEPENDS coverage.info coverage.tar.gz)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
find_program(LCOV lcov REQUIRED)
find_program(SED sed REQUIRED)
add_custom_target(
coverage
COMMAND "${LCOV}" --capture --directory . --output-file coverage.info
--branch-coverage --rc geninfo_unexecuted_blocks=1
COMMAND "${LCOV}" --remove coverage.info '/usr/*' --output-file
coverage.info
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
DEPENDS "${linyaps-box_APP}" test)
else()
message(
FATAL_ERROR "Coverage is not supported for ${CMAKE_CXX_COMPILER_ID}.")
endif()
endfunction()
setup_linyaps_box_coverage()

211
CMakePresets.json Normal file
View File

@ -0,0 +1,211 @@
{
"version": 10,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [
{
"name": "dev",
"displayName": "The developer profile",
"description": "The default configuration for developers",
"binaryDir": "${sourceDir}/build-dev",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_COLOR_DIAGNOSTICS": true,
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Werror -O0 -g3 -fsanitize=address,undefined $env{CXXFLAGS}",
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
"linyaps-box_ENABLE_COVERAGE": true,
"linyaps-box_ENABLE_CPACK": true,
"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_ENABLE_CPM": "ON"
}
},
{
"name": "release",
"displayName": "The release profile",
"description": "The default configuration for release",
"binaryDir": "${sourceDir}/build-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_COLOR_DIAGNOSTICS": true,
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
"linyaps-box_ENABLE_CPACK": "ON",
"linyaps-box_MAKE_RELEASE": "ON"
}
},
{
"name": "static",
"displayName": "The static build profile",
"description": "The default configuration for produce static binary",
"binaryDir": "${sourceDir}/build-static",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "MinSizeRel",
"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_ENABLE_CPACK": "ON",
"linyaps-box_MAKE_RELEASE": "ON"
}
},
{
"name": "ci",
"displayName": "The CI profile",
"description": "The default configuration for CI",
"binaryDir": "${sourceDir}/build-ci",
"cacheVariables": {
"CMAKE_COLOR_DIAGNOSTICS": true,
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Werror -fsanitize=address,undefined $env{CXXFLAGS}",
"linyaps-box_ENABLE_COVERAGE": true,
"linyaps-box_ENABLE_SMOKE_TESTS": true
}
}
],
"buildPresets": [
{
"name": "dev",
"configurePreset": "dev"
},
{
"name": "release",
"configurePreset": "release"
},
{
"name": "ci",
"configurePreset": "ci"
},
{
"name": "static",
"configurePreset": "static"
}
],
"testPresets": [
{
"name": "dev",
"configurePreset": "dev",
"output": {
"outputOnFailure": true
},
"execution": {
"noTestsAction": "default",
"stopOnFailure": true
}
},
{
"name": "release",
"configurePreset": "release",
"output": {
"outputOnFailure": true
},
"execution": {
"noTestsAction": "default",
"stopOnFailure": true
}
},
{
"name": "ci",
"configurePreset": "ci",
"output": {
"verbosity": "verbose"
},
"execution": {
"noTestsAction": "default",
"stopOnFailure": false
}
},
{
"name": "static",
"configurePreset": "static",
"output": {
"verbosity": "verbose"
},
"execution": {
"noTestsAction": "default",
"stopOnFailure": true
}
}
],
"workflowPresets": [
{
"name": "dev",
"displayName": "The default workflow for developers",
"description": "Configure, build then test with dev profile",
"steps": [
{
"type": "configure",
"name": "dev"
},
{
"type": "build",
"name": "dev"
},
{
"type": "test",
"name": "dev"
}
]
},
{
"name": "release",
"displayName": "The default workflow for release",
"description": "Configure, build then test with release profile",
"steps": [
{
"type": "configure",
"name": "release"
},
{
"type": "build",
"name": "release"
},
{
"type": "test",
"name": "release"
}
]
},
{
"name": "ci",
"displayName": "The default workflow for CI",
"description": "Configure, build then test with CI profile",
"steps": [
{
"type": "configure",
"name": "ci"
},
{
"type": "build",
"name": "ci"
},
{
"type": "test",
"name": "ci"
}
]
},
{
"name": "static",
"displayName": "The default workflow for static build",
"description": "Configure, build then test with static profile",
"steps": [
{
"type": "configure",
"name": "static"
},
{
"type": "build",
"name": "static"
},
{
"type": "test",
"name": "static"
}
]
}
]
}

11
LICENSES/BSD-3-Clause.txt Normal file
View File

@ -0,0 +1,11 @@
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -2,8 +2,17 @@ MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,79 +1,24 @@
# 玲珑:沙箱
# linyaps-box
玲珑Linglong is the container application toolkit of deepin.
\[ **en** | [zh_CN](./README.zh_CN.md) \]
玲珑是玲珑塔的简称,既表示容器能对应用有管控作用,也表明了应用/运行时/系统向塔一样分层的思想。
[![Packaging status](https://repology.org/badge/vertical-allrepos/linyaps-box.svg)](https://repology.org/project/linyaps-box/versions)
## Feature
This project is a simple [OCI runtime] mainly used by [linyaps],
which is a toolkit for Linux desktop application distributing.
- [x] Standard oci runtime
[OCI runtime]: https://github.com/opencontainers/runtime-spec
[linyaps]: https://github.com/OpenAtom-Linyaps/linyaps
## Roadmap
## Build
### Current
- [ ] OCI Standard Box
## Dependencies
It is recommend to use [cmake-presets]:
```bash
#For release based on debian
sudo apt-get install cmake build-essential libyaml-cpp-dev nlohmann-json3-dev libgtest-dev
cmake --workflow --preset=dev
```
## Installation
## Roadmap
### Current
- [ ] Configuration
- [x] Root
- [ ] Mount
- [ ] Cgroup
- [X] Hostname
- [ ] Linux Container
- [x] Default Filesystems
- [X] Namespaces
- [x] Network
- [X] User namespace mappings
- [ ] Devices
- [ ] Default Devices
- [ ] Control groups
- [ ] IntelRdt
- [ ] Sysctl
- [ ] Seccomp
- [ ] Rootfs Mount Propagation
- [ ] Masked Paths
- [ ] Readonly Paths
- [ ] Mount Label
- [ ] Extend
- [x] Container manager
- [x] Debug
- [ ] DBus proxy permission control
- [ ] Filesystem permission control base fuse
- [ ] X11&&Wayland security
### TODO
- [ ] full support of parse all seccomp arch and syscall
## Getting help
Any usage issues can ask for help via
- [Gitter](https://gitter.im/orgs/linuxdeepin/rooms)
- [IRC channel](https://webchat.freenode.net/?channels=deepin)
- [Forum](https://bbs.deepin.org)
- [WiKi](https://wiki.deepin.org/)
## Getting involved
We encourage you to report issues and contribute changes
- [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en)
. (English)
- [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文)
[cmake-presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
## License

View File

@ -1,78 +1,25 @@
# 玲珑:沙箱
# linyaps-box
玲珑是玲珑塔的简称,既表示容器能对应用有管控作用,也表明了应用/运行时/系统向塔一样分层的思想。
\[ [en](./README.md) | **zh_CN** \]
## Feature
[![Packaging status](https://repology.org/badge/vertical-allrepos/linyaps-box.svg)](https://repology.org/project/linyaps-box/versions)
- [x] Standard oci runtime
这个项目是一个简单的[OCI运行时],主要由[玲珑]使用,
玲珑是一个用于Linux桌面应用程序分发的工具包。
## Roadmap
[OCI运行时]: https://github.com/opencontainers/runtime-spec
[玲珑]: https://github.com/OpenAtom-Linyaps/linyaps
### Current
## 构建
- [ ] OCI Standard Box
## Dependencies
推荐使用[cmake-presets]:
```bash
#For release based on debian
sudo apt-get install cmake build-essential libyaml-cpp-dev nlohmann-json3-dev libgtest-dev
cmake --workflow --preset=dev
```
## Installation
[cmake-presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
## Roadmap
## 许可证
### Current
- [ ] Configuration
- [x] Root
- [ ] Mount
- [ ] Cgroup
- [X] Hostname
- [ ] Linux Container
- [x] Default Filesystems
- [X] Namespaces
- [x] Network
- [X] User namespace mappings
- [ ] Devices
- [ ] Default Devices
- [ ] Control groups
- [ ] IntelRdt
- [ ] Sysctl
- [ ] Seccomp
- [ ] Rootfs Mount Propagation
- [ ] Masked Paths
- [ ] Readonly Paths
- [ ] Mount Label
- [ ] Extend
- [x] Container manager
- [x] Debug
- [ ] DBus proxy permission control
- [ ] Filesystem permission control base fuse
- [ ] X11&&Wayland security
### TODO
- [ ] full support of parse all seccomp arch and syscall
## Getting help
Any usage issues can ask for help via
- [Gitter](https://gitter.im/orgs/linuxdeepin/rooms)
- [IRC channel](https://webchat.freenode.net/?channels=deepin)
- [Forum](https://bbs.deepin.org)
- [WiKi](https://wiki.deepin.org/)
## Getting involved
We encourage you to report issues and contribute changes
- [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en)
. (English)
- [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文)
## License
This project is licensed under [LGPL-3.0-or-later](LICENSE).
本项目使用[LGPL-3.0-or-later](LICENSE)许可证授权。

73
REUSE.toml Normal file
View File

@ -0,0 +1,73 @@
version = 1
SPDX-PackageName = "linglong-box"
SPDX-PackageSupplier = "UnionTech Software Technology Co., Ltd. <>"
SPDX-PackageDownloadLocation = "https://github.com/linuxdeepin/linglong-box"
[[annotations]]
path = [
".github/**",
".gitlab-ci.yml",
"tests/ll-box-st/**.json",
"tests/ll-box-st/*.txt",
"tests/ll-box-ut/data/**",
".obs/workflows.yml",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = [
"README.md",
"README.zh_CN.md",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC-BY-4.0"
[[annotations]]
path = [
"CMakePresets.json",
"CMakeLists.txt",
".gitignore",
"tests/ll-box-ut/.gitignore",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = [".clang-format", ".cmake-format.py"]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ["cmake/CPM.cmake"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2019-2023 Lars Melchior and contributors"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = ["cmake.external/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ["external/nlohmann_json/include/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2013-2023 Niels Lohmann <https://nlohmann.me>"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = ["external/CLI11/include/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2017-2025 University of Cincinnati, developed by Henry Schreiner under NSF AWARD 1414736."
SPDX-License-Identifier = "BSD-3-Clause"
[[annotations]]
path = ["external/*/CMakeLists.txt"]
precedence = "aggregate"
SPDX-FileCopyrightText = "UnionTech Software Technology Co., Ltd."
SPDX-License-Identifier = "CC0-1.0"

10
app/ll-box/src/main.cpp Normal file
View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/app.h"
int main(int argc, char **argv)
{
return linyaps_box::main(argc, argv);
}

View File

@ -0,0 +1,9 @@
if(TARGET CLI11::CLI11)
set(CLI11_FOUND TRUE)
# Set version variables
set(CLI11_VERSION_MAJOR 2)
set(CLI11_VERSION_MINOR 5)
set(CLI11_VERSION_PATCH 0)
set(CLI11_VERSION "2.5.0")
endif()

View File

@ -0,0 +1,9 @@
if(TARGET nlohmann_json::nlohmann_json)
set(nlohmann_json_FOUND TRUE)
# Set version variables
set(nlohmann_json_VERSION_MAJOR 3)
set(nlohmann_json_VERSION_MINOR 12)
set(nlohmann_json_VERSION_PATCH 0)
set(nlohmann_json_VERSION "3.12.0")
endif()

1363
cmake/CPM.cmake Normal file

File diff suppressed because it is too large Load Diff

149
debian/changelog vendored
View File

@ -1,149 +0,0 @@
linglong-box (1.3.3.15-1) unstable; urgency=medium
* update version.
-- huqinghong <huqinghong@uniontech.com> Tue, 27 Dec 2022 14:54:52 +0800
linglong-box (1.3.3.14-1) unstable; urgency=medium
* update version.
-- yuanqiliang <yuanqiliang@uniontech.com> Thu, 25 Nov 2022 11:02:00 +0800
linglong-box (1.3.3.13-1) unstable; urgency=medium
* update version.
-- Chen Linxuan <chenlinxuan@uniontech.com> Thu, 24 Nov 2022 11:02:00 +0800
linglong-box (1.3.3.12-1) unstable; urgency=medium
* update version.
-- Chen Linxuan <chenlinxuan@uniontech.com> Thu, 27 Oct 2022 14:58:00 +0800
linglong-box (1.3.3.11-1) unstable; urgency=medium
* update version to 1.3.3.11-1 for deepin.
-- huqinghong <huqinghong@uniontech.com> Wed, 26 Oct 2022 15:33:06 +0800
linglong-box (1.3.3.10-1) unstable; urgency=medium
* update version.
-- chenhuixing <chenhuixing@uniontech.com> Fri, 23 Sep 2022 11:19:11 +0800
linglong-box (1.3.3.9-1) unstable; urgency=medium
* update version.
-- chenlinxuan <chenlinxuan@uniontech.com> Thu, 22 Sep 2022 11:09:15 +0800
linglong-box (1.3.3.7-1) unstable; urgency=medium
* update version.
-- chenhuixing <chenhuixing@uniontech.com> Tue, 06 Sep 2022 10:58:15 +0800
linglong-box (1.3.3.5-1) unstable; urgency=medium
* update version to 1.3.3.5-1 for deepin.
-- liujianqiang <liujianqiang@uniontech.com> Mon, 01 Aug 2022 20:50:57 +0800
linglong-box (1.3.3-1) unstable; urgency=medium
* update version to 1.3.3-1 for deepin.
-- liujianqiang <liujianqiang@uniontech.com> Thu, 28 Jul 2022 15:29:46 +0800
linglong-box (1.3.2-1) unstable; urgency=medium
* update version.
-- liujianqiang <liujianqiang@uniontech.com> Fri, 24 Jun 2022 16:56:42 +0800
linglong-box (1.3.0-1) unstable; urgency=medium
* update version.
-- liujianqiang <liujianqiang@uniontech.com> Fri, 27 May 2022 17:45:46 +0800
linglong-box (1.2.7-1) unstable; urgency=medium
* fix build issue.
-- liujianqiang <liujianqiang@uniontech.com> Fri, 15 Apr 2022 18:33:27 +0800
linglong-box (1.2.6-1) unstable; urgency=medium
* 1. adjust wait pid strategy.
* 2. print pidns in log.
* 3. configure uid/gid mapping after clone.
* 4. remove CLONE-FS flag when cloning NonePrivilegeProc.
* 5. wrong /dev/ptmx link.
-- liujianqiang <liujianqiang@uniontech.com> Tue, 22 Mar 2022 13:38:53 +0800
linglong-box (1.2.5-1) unstable; urgency=medium
* 1. rename json fields.
* 2. close leaked fds.
* 3. remove forced CLONE_NEWPID flag for second clone.
* 4. add waitpid helper.
* 5. add support for dbus proxy.
* 6. remove unused code.
-- liujianqiang <liujianqiang@uniontech.com> Tue, 15 Mar 2022 10:04:16 +0800
linglong-box (1.2.4-1) unstable; urgency=medium
* 1. void c_str() of destoryed string
-- chenlinxuan <chenlinxuan@uniontech.com> Tue, 08 Mar 2022 13:27:24 +0800
linglong-box (1.2.3-1) unstable; urgency=medium
* 1. make fuse-overlayfs defalut.
* 2. fix update error info.
* 3. fix do mount by proc fd.
-- liujianqiang <liujianqiang@uniontech.com> Tue, 08 Mar 2022 13:27:24 +0800
linglong-box (1.2.2-1) unstable; urgency=medium
* 1. fix read/write /dev/null failed.
* 2. add log fliter.
* 3. fix mount mqueue amd cgroup in rootless mode.
* 4. fix make mountnode const again.
* 5. fix make ro bind work.
-- liujianqiang <liujianqiang@uniontech.com> Wed, 16 Feb 2022 13:47:58 +0800
linglong-box (1.2.1-1) unstable; urgency=medium
* 1. fuse proxy support
* 2. fix construct string with nullptr cause crash
* 3. fallback to bind when sysfs mount failed
* 4. root mount for ll-fuse-proxy
-- liujianqiang <liujianqiang@uniontech.com> Mon, 24 Jan 2022 08:55:58 +0800
linglong-box (1.2.0-1) unstable; urgency=medium
* mount add a MS_REC flag
-- liujianqiang <liujianqiang@uniontech.com> Fri, 14 Jan 2022 10:51:13 +0800
linglong-box (1.0.1-1) unstable; urgency=medium
* support rootless and fuse
-- iceyer <me@iceyer.net> Wed, 29 Dec 2021 17:57:01 +0800
linglong-box (1.0-1) unstable; urgency=medium
* Init package.
-- chenhuixing <chenhuixing@uniontech.com> Thu, 09 Sep 2021 11:41:48 +0000

1
debian/compat vendored
View File

@ -1 +0,0 @@
11

19
debian/control vendored
View File

@ -1,19 +0,0 @@
Source: linglong-box
Section: admin
Priority: optional
Maintainer: Deepin Packages Builder <packages@deepin.org>
Build-Depends:
debhelper (>= 11),
cmake,
libyaml-cpp-dev,
pkg-config,
libseccomp-dev,
libgtest-dev
Standards-Version: 4.1.3
Homepage: https://www.deepin.org
Package: linglong-box
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Deepin sandbox with OCI standard.
Deepin sandbox...

21
debian/copyright vendored
View File

@ -1,21 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: linglong-box
Files: *
Copyright: 2017 Deepin Technology Co., Ltd.
License: GPL-3+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

View File

@ -1 +0,0 @@
linglong-box: statically-linked-binary usr/bin/ll-box-static

12
debian/rules vendored
View File

@ -1,12 +0,0 @@
#!/usr/bin/make -f
include /usr/share/dpkg/default.mk
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
%:
dh $@
override_dh_auto_configure:
dh_auto_configure -- \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DAPP_VERSION=$(DEB_VERSION_UPSTREAM) -DVERSION=$(DEB_VERSION_UPSTREAM) LIB_INSTALL_DIR=/usr/lib DH_AUTO_ARGS

View File

@ -1 +0,0 @@
3.0 (quilt)

View File

@ -1,21 +0,0 @@
# Filesystem Drivers
For a container, there are always need to create some file not exist. So we need use a virtual filesystem to mount file not exist or override lower file.
In flatpak, it not allow to override file, that is a good imagine, but how to bind a file not exist in the base, the flatpak can not deal with that now.
For docker or other container tools, an overlayfs (or other filesystem with same function) is need.
For linglong, we do not need a powerful overlayfs to action complex function, but just fix some issue that case by some not standard apps. so we use an mini fuse proxy to fake mount point or path not exist.
So apps should keep the rules first. And just keep compatibility with some not open source apps by fuse proxy filesystem.
## The access control
The access control is not an mandatory standard. OCI runtime spec do not care it, especially an dynamic access control. We use fuse proxy filesystem to finish this.
When app in container access an path not exist, the fuse proxy can get it. It the path is define as an permission control path, it will as user to confirm. and notify ll-box freeze all process in sandbox.
## How to use fuse-proxy?
fuse-proxy is a mount backend for ll-box. the ll-box call it before chroot in the new mount namespace.

4
external/.clang-format vendored Normal file
View File

@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: None
#
# SPDX-License-Identifier: CC0-1.0
DisableFormat: true

6
external/.cmake-format.py vendored Normal file
View File

@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: None
#
# SPDX-License-Identifier: CC0-1.0
with section("format"):
disable = True

3
external/CLI11/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,3 @@
add_library(CLI11 INTERFACE)
add_library(CLI11::CLI11 ALIAS CLI11)
target_include_directories(CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include/")

11527
external/CLI11/include/CLI/CLI.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

5
external/nlohmann_json/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
# https://github.com/nlohmann/json/tree/v3.11.3?tab=readme-ov-file#integration
add_library(nlohmann_json INTERFACE)
add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json)
target_include_directories(nlohmann_json INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include/")

View File

@ -0,0 +1,187 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
// #include <nlohmann/detail/abi_macros.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// This file contains all macro definitions affecting or depending on the ABI
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
#warning "Already included a different version of the library!"
#endif
#endif
#endif
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
#endif
#ifndef JSON_DIAGNOSTIC_POSITIONS
#define JSON_DIAGNOSTIC_POSITIONS 0
#endif
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
#endif
#if JSON_DIAGNOSTIC_POSITIONS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
#else
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
#else
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
#endif
// Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
_v ## major ## _ ## minor ## _ ## patch
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_VERSION
#else
#define NLOHMANN_JSON_NAMESPACE_VERSION \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
NLOHMANN_JSON_VERSION_MINOR, \
NLOHMANN_JSON_VERSION_PATCH)
#endif
// Combine namespace components
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
#ifndef NLOHMANN_JSON_NAMESPACE
#define NLOHMANN_JSON_NAMESPACE \
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION)
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
namespace nlohmann \
{ \
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION) \
{
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_END
#define NLOHMANN_JSON_NAMESPACE_END \
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
} // namespace nlohmann
#endif
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
NLOHMANN_JSON_NAMESPACE_BEGIN
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
/// a class to store JSON values
/// @sa https://json.nlohmann.me/api/basic_json/
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
class CustomBaseClass = void>
class basic_json;
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
/// @sa https://json.nlohmann.me/api/json_pointer/
template<typename RefStringType>
class json_pointer;
/*!
@brief default specialization
@sa https://json.nlohmann.me/api/json/
*/
using json = basic_json<>;
/// @brief a minimal map-like container that preserves insertion order
/// @sa https://json.nlohmann.me/api/ordered_map/
template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map;
/// @brief specialization that maintains the insertion order of object keys
/// @sa https://json.nlohmann.me/api/ordered_json/
using ordered_json = basic_json<nlohmann::ordered_map>;
NLOHMANN_JSON_NAMESPACE_END
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_

View File

@ -1,34 +0,0 @@
pkg_check_modules(SECCOMP REQUIRED libseccomp)
set(LINK_LIBS
seccomp
stdc++)
set(LL_BOX_SOURCES
# cmake-format: sortable
main.cpp
loader.cpp
util/common.cpp
util/filesystem.cpp
util/semaphore.cpp
util/debug/debug.cpp
util/platform.cpp
util/logger.cpp
util/message_reader.cpp
container/container.cpp
container/mount/host_mount.cpp
container/mount/filesystem_driver.cpp
container/seccomp.cpp
)
add_executable(ll-box ${LL_BOX_SOURCES})
target_link_libraries(ll-box ${LINK_LIBS})
if (BUILD_STATIC)
add_executable(ll-box-static ${LL_BOX_SOURCES})
target_link_libraries(ll-box-static -static ${LINK_LIBS})
install(TARGETS ll-box-static RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/libexec)
endif (BUILD_STATIC)
install(TARGETS ll-box RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
#install(TARGETS ll-box RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID)

View File

@ -1,86 +0,0 @@
# ll-box
ll-box is a standard oci runtime.
## Feature
- [x] No root daemon, use setuid
- [x] Standard oci runtime
## Roadmap
### Current
- [ ] Configuration
- [x] Root
- [ ] Mount
- [x] Cgroup
- [ ] All support
- [x] Hostname
- [ ] Linux Container
- [x] Default Filesystems
- [x] Namespaces
- [x] Network
- [x] User namespace mappings
- [ ] Devices
- [ ] Default Devices
- [ ] Control groups v2
- [ ] cpu
- [ ] memory
- [ ] pids
- [ ] devices
- [ ] io
- [ ] cpuset
- [ ] rdma
- [ ] perf_event
- [ ] IntelRdt
- [ ] Sysctl
- [x] Seccomp
- [ ] full support of all syscall
- [ ] full support of arch
- [ ] Rootfs Mount Propagation
- [ ] Masked Paths
- [ ] Readonly Paths
- [ ] Mount Label
### Next
- [ ] TODO
## Dependencies
- [ ] C++11/STL
- [ ] nlonmann-json3
- [ ] gtest
```bash
#For release based on debian
sudo apt-get cmake build-essential \
nlonmann-json3-dev \
libgtest-dev
```
## Getting help
Any usage issues can ask for help via
- [Gitter](https://gitter.im/orgs/linuxdeepin/rooms)
- [IRC channel](https://webchat.freenode.net/?channels=deepin)
- [Forum](https://bbs.deepin.org)
- [WiKi](https://wiki.deepin.org/)
## Getting involved
We encourage you to report issues and contribute changes
- [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en)
. (English)
- [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文)
## License
Deepin Boot Maker is licensed under GPLv3.
## Credits and references
- [OStree](https://github.com/ostreedev/ostree)

View File

@ -1,845 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "container.h"
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <grp.h>
#include <sched.h>
#include <unistd.h>
#include <cerrno>
#include <map>
#include <utility>
#include "util/logger.h"
#include "util/filesystem.h"
#include "util/semaphore.h"
#include "util/debug/debug.h"
#include "util/platform.h"
#include "container/seccomp.h"
#include "container/container_option.h"
#include "container/mount/host_mount.h"
#include "container/mount/filesystem_driver.h"
namespace linglong {
static const std::string llDbusProxyBin = "/usr/bin/ll-dbus-proxy";
// start dbus proxy
static int StartDbusProxy(const Runtime &runtime)
{
if (!(runtime.annotations.has_value() && runtime.annotations->dbus_proxy_info.has_value()
&& runtime.annotations->dbus_proxy_info->enable)) {
logInf() << "dbus proxy disabled";
return -1;
}
const auto &info = runtime.annotations->dbus_proxy_info;
std::string socket_path = info->proxy_path;
pid_t proxy_pid = fork();
if (proxy_pid < 0) {
logErr() << "fork to start dbus proxy failed:", util::errnoString();
return -1;
}
if (0 == proxy_pid) {
// FIXME: parent may dead before this return.
prctl(PR_SET_PDEATHSIG, SIGKILL);
std::string bus_type = info->bus_type;
std::string app_id = info->app_id;
std::vector<std::string> name_fliter = info->name;
std::vector<std::string> path_fliter = info->path;
std::vector<std::string> interface_fliter = info->interface;
std::string name_fliter_string = linglong::util::str_vec_join(name_fliter, ',');
std::string path_fliter_string = linglong::util::str_vec_join(path_fliter, ',');
std::string interface_fliter_string = linglong::util::str_vec_join(interface_fliter, ',');
char const *const args[] = {llDbusProxyBin.c_str(),
app_id.c_str(),
bus_type.c_str(),
socket_path.c_str(),
name_fliter_string.c_str(),
path_fliter_string.c_str(),
interface_fliter_string.c_str(),
NULL};
int ret = execvp(args[0], (char **)args);
logErr() << "start dbus proxy failed, ret=" << ret;
exit(ret);
} else {
// FIXME: this call make 10ms lag at least
if (util::fs::path(socket_path).wait_until_exsit() != 0) {
logErr() << util::format("timeout! socketPath [\"%s\"] not exsit", socket_path.c_str());
return -1;
}
}
return 0;
}
int DropToNormalUser(int uid, int gid)
{
setuid(uid);
seteuid(uid);
setgid(gid);
setegid(gid);
return 0;
}
static int ConfigUserNamespace(const linglong::Linux &linux, int initPid)
{
std::string pid = "self";
if (initPid > 0) {
pid = util::format("%d", initPid);
}
logDbg() << "old uid:" << getuid() << "gid:" << getgid();
logDbg() << "start write uid_map and pid_map" << initPid;
// write uid map
std::ofstream uidMapFile(util::format("/proc/%s/uid_map", pid.c_str()));
for (auto const &idMap : linux.uidMappings) {
uidMapFile << util::format("%lu %lu %lu\n", idMap.containerID, idMap.hostID, idMap.size);
}
uidMapFile.close();
// write gid map
auto setgroupsPath = util::format("/proc/%s/setgroups", pid.c_str());
std::ofstream setgroupsFile(setgroupsPath);
setgroupsFile << "deny";
setgroupsFile.close();
std::ofstream gidMapFile(util::format("/proc/%s/gid_map", pid.c_str()));
for (auto const &idMap : linux.gidMappings) {
gidMapFile << util::format("%lu %lu %lu\n", idMap.containerID, idMap.hostID, idMap.size);
}
gidMapFile.close();
logDbg() << "new uid:" << getuid() << "gid:" << getgid();
return 0;
}
// FIXME(iceyer): not work now
static int ConfigCgroupV2(const std::string &cgroupsPath, const linglong::Resources &res, int initPid)
{
if (cgroupsPath.empty()) {
logWan() << "skip with empty cgroupsPath";
return 0;
}
auto writeConfig = [](const std::map<std::string, std::string> &cfgs) {
for (auto const &cfg : cfgs) {
logWan() << "ConfigCgroupV2" << cfg.first << cfg.second;
std::ofstream cfgFile(cfg.first);
cfgFile << cfg.second << std::endl;
cfgFile.close();
}
};
auto cgroupsRoot = util::fs::path(cgroupsPath);
util::fs::create_directories(cgroupsRoot, 0755);
int ret = mount("cgroup2", cgroupsRoot.string().c_str(), "cgroup2", 0u, nullptr);
if (ret != 0) {
logErr() << "mount cgroup failed" << util::RetErrString(ret);
return -1;
}
// TODO: should sub path with pid?
auto subCgroupRoot = cgroupsRoot / "ll-box";
if (!util::fs::create_directories(util::fs::path(subCgroupRoot), 0755)) {
logErr() << "create_directories subCgroupRoot failed" << util::errnoString();
return -1;
}
auto subCgroupPath = [subCgroupRoot](const std::string &compents) -> std::string {
return (subCgroupRoot / compents).string();
};
// config memory
const auto memMax = res.memory.limit;
if (memMax > 0) {
const auto memSwapMax = res.memory.swap - memMax;
const auto memLow = res.memory.reservation;
writeConfig({
{subCgroupPath("memory.max"), util::format("%d", memMax)},
{subCgroupPath("memory.swap.max"), util::format("%d", memSwapMax)},
{subCgroupPath("memory.low"), util::format("%d", memLow)},
});
}
// config cpu
const auto cpuPeriod = res.cpu.period;
const auto cpuMax = res.cpu.quota;
// convert from [2-262144] to [1-10000], 262144 is 2^18
const auto cpuWeight = (1 + ((res.cpu.shares - 2) * 9999) / 262142);
writeConfig({
{subCgroupPath("cpu.max"), util::format("%d %d", cpuMax, cpuPeriod)},
{subCgroupPath("cpu.weight"), util::format("%d", cpuWeight)},
});
// config pid
writeConfig({
{subCgroupPath("cgroup.procs"), util::format("%d", initPid)},
});
logDbg() << "move" << initPid << "to new cgroups";
return 0;
}
inline void epoll_ctl_add(int epfd, int fd)
{
static epoll_event ev = {};
ev.events = EPOLLIN;
ev.data.fd = fd;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
if (ret != 0)
logWan() << util::errnoString();
}
// if wstatus says child exit normally, return true else false
static bool parse_wstatus(const int &wstatus, std::string &info)
{
if (WIFEXITED(wstatus)) {
auto code = WEXITSTATUS(wstatus);
info = util::format("exited with code %d", code);
return code == 0;
} else if (WIFSIGNALED(wstatus)) {
info = util::format("terminated by signal %d", WTERMSIG(wstatus));
return false;
} else {
info = util::format("is dead with wstatus=%d", wstatus);
return false;
}
}
struct ContainerPrivate {
public:
ContainerPrivate(Runtime r, std::unique_ptr<util::MessageReader> reader, Container * /*parent*/)
: hostRoot(r.root.path)
, runtime(std::move(r))
, nativeMounter(new HostMount)
, overlayfsMounter(new HostMount)
, fuseproxyMounter(new HostMount)
, reader(std::move(reader))
{
}
std::string hostRoot;
Runtime runtime;
// bool use_delay_new_user_ns = false;
bool useNewCgroupNs = false;
Option option;
uid_t hostUid = -1;
gid_t hostGid = -1;
std::unique_ptr<HostMount> nativeMounter;
std::unique_ptr<HostMount> overlayfsMounter;
std::unique_ptr<HostMount> fuseproxyMounter;
HostMount *containerMounter = nullptr;
std::unique_ptr<util::MessageReader> reader;
std::map<int, std::string> pidMap;
public:
static int DropPermissions()
{
__gid_t newgid[1] = {getgid()};
auto newuid = getuid();
auto olduid = geteuid();
if (0 == olduid) {
setgroups(1, newgid);
}
seteuid(newuid);
return 0;
}
int PrepareLinks() const
{
chdir("/");
if (option.linkLfs) {
symlink("/usr/bin", "/bin");
symlink("/usr/lib", "/lib");
symlink("/usr/lib32", "/lib32");
symlink("/usr/lib64", "/lib64");
symlink("/usr/libx32", "/libx32");
}
// default link
symlink("/proc/kcore", "/dev/core");
symlink("/proc/self/fd", "/dev/fd");
symlink("/proc/self/fd/2", "/dev/stderr");
symlink("/proc/self/fd/0", "/dev/stdin");
symlink("/proc/self/fd/1", "/dev/stdout");
return 0;
}
int PrepareDefaultDevices() const
{
struct Device {
std::string path;
mode_t mode;
dev_t dev;
};
std::vector<Device> list = {
{"/dev/null", S_IFCHR | 0666, makedev(1, 3)}, {"/dev/zero", S_IFCHR | 0666, makedev(1, 5)},
{"/dev/full", S_IFCHR | 0666, makedev(1, 7)}, {"/dev/random", S_IFCHR | 0666, makedev(1, 8)},
{"/dev/urandom", S_IFCHR | 0666, makedev(1, 9)}, {"/dev/tty", S_IFCHR | 0666, makedev(5, 0)},
};
// TODO(iceyer): not work now
if (!option.rootless) {
for (auto const &dev : list) {
auto path = hostRoot + dev.path;
int ret = mknod(path.c_str(), dev.mode, dev.dev);
if (0 != ret) {
logErr() << "mknod" << path << dev.mode << dev.dev << "failed with" << util::RetErrString(ret);
}
chmod(path.c_str(), dev.mode | 0xFFFF);
chown(path.c_str(), 0, 0);
}
} else {
for (auto const &dev : list) {
Mount mount;
mount.destination = dev.path;
mount.type = "bind";
mount.data = std::vector<std::string> {};
mount.flags = MS_BIND;
mount.fsType = Mount::Bind;
mount.source = dev.path;
this->containerMounter->MountNode(mount);
}
}
// FIXME(iceyer): /dev/console is set up if terminal is enabled in the config by bind mounting the
// pseudoterminal slave to /dev/console.
symlink("/dev/pts/ptmx", (this->hostRoot + "/dev/ptmx").c_str());
return 0;
}
void waitChildAndExec()
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGTERM);
// FIXME: add more signals.
/* Block signals so that they aren't handled
according to their default dispositions. */
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
logWan() << "sigprocmask block";
int sfd = signalfd(-1, &mask, 0);
if (sfd == -1)
logWan() << "signalfd";
auto epfd = epoll_create(1);
epoll_ctl_add(epfd, sfd);
if (reader.get() != nullptr)
epoll_ctl_add(epfd, reader->fd);
for (;;) {
struct epoll_event events[10];
int event_cnt = epoll_wait(epfd, events, 5, -1);
for (int i = 0; i < event_cnt; i++) {
const auto &event = events[i];
if (event.data.fd == sfd) {
struct signalfd_siginfo fdsi;
ssize_t s = read(sfd, &fdsi, sizeof(fdsi));
if (s != sizeof(fdsi)) {
logWan() << "error read from signal fd";
}
if (fdsi.ssi_signo == SIGCHLD) {
logWan() << "recive SIGCHLD";
int wstatus;
while (int child = waitpid(-1, &wstatus, WNOHANG)) {
if (child > 0) {
std::string info;
auto normal = parse_wstatus(wstatus, info);
info = util::format("child [%d] [%s].", child, info.c_str());
if (normal) {
logDbg() << info;
} else {
logWan() << info;
}
auto it = pidMap.find(child);
if (it != pidMap.end()) {
if (reader.get() != nullptr)
reader->writeChildExit(child, it->second, wstatus, info);
pidMap.erase(it);
}
} else if (child < 0) {
if (errno == ECHILD) {
logDbg() << util::format("no child to wait");
return;
} else {
auto str = util::errnoString();
logErr() << util::format("waitpid failed, %s", str.c_str());
return;
}
}
}
} else if (fdsi.ssi_signo == SIGTERM) {
// FIXME: box should exit with failed return code.
logWan() << util::format("Terminated\n");
return;
} else {
logWan() << util::format("Read unexpected signal [%d]\n", fdsi.ssi_signo);
}
} else if (reader.get() != nullptr && event.data.fd == reader->fd) {
auto json = reader->read();
if (json.empty()) {
break;
}
auto process = json.get<Process>();
forkAndExecProcess(process, true);
} else {
logWan() << "Unknown fd";
}
}
}
return;
}
bool forkAndExecProcess(const Process process, bool unblock = false)
{
// FIXME: parent may dead before this return.
prctl(PR_SET_PDEATHSIG, SIGKILL);
int pid = fork();
if (pid < 0) {
logErr() << "fork failed" << util::RetErrString(pid);
return false;
}
if (0 == pid) {
if (unblock) {
// FIXME: As we use signalfd, we have to block signal, but child created by fork
// will inherit blocked signal set, so we have to unblock it. This is just a
// workround.
sigset_t mask;
sigfillset(&mask);
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
logWan() << "sigprocmask unblock";
}
logDbg() << "process.args:" << process.args;
int ret;
ret = chdir(process.cwd.c_str());
if (ret) {
logErr() << "failed to chdir to" << process.cwd.c_str();
}
// for PATH
for (const auto &env : process.env) {
auto kv = util::str_spilt(env, "=");
if (kv.size() == 2)
setenv(kv.at(0).c_str(), kv.at(1).c_str(), 1);
else if (kv.size() == 1) {
setenv(kv.at(0).c_str(), "", 1);
} else {
logWan() << "Unknown env:" << env;
}
}
logInf() << "start exec process";
ret = util::Exec(process.args, process.env);
if (0 != ret) {
logErr() << "exec failed" << util::RetErrString(ret);
}
exit(ret);
} else {
pidMap.insert(make_pair(pid, process.args[0]));
}
return true;
}
int PivotRoot() const
{
int ret = -1;
chdir(hostRoot.c_str());
if (option.rootless && runtime.annotations->overlayfs.has_value()) {
int flag = MS_MOVE;
ret = mount(".", "/", nullptr, flag, nullptr);
if (0 != ret) {
logErr() << "mount / failed" << util::RetErrString(ret);
return -1;
}
ret = chroot(".");
if (0 != ret) {
logErr() << "chroot . failed" << util::RetErrString(ret);
return -1;
}
} else {
int flag = MS_BIND | MS_REC;
ret = mount(".", ".", "bind", flag, nullptr);
if (0 != ret) {
logErr() << "mount / failed" << util::RetErrString(ret);
return -1;
}
auto llHostFilename = "ll-host";
auto llHostPath = hostRoot + "/" + llHostFilename;
mkdir(llHostPath.c_str(), 0755);
ret = syscall(SYS_pivot_root, hostRoot.c_str(), llHostPath.c_str());
if (0 != ret) {
logErr() << "SYS_pivot_root failed" << hostRoot << util::errnoString() << errno << ret;
return -1;
}
chdir("/");
ret = chroot(".");
if (0 != ret) {
logErr() << "chroot failed" << hostRoot << util::errnoString() << errno;
return -1;
}
chdir("/");
umount2(llHostFilename, MNT_DETACH);
}
return 0;
}
int PrepareRootfs()
{
auto PrepareOverlayfsRootfs = [&](const AnnotationsOverlayfs &overlayfs) -> int {
nativeMounter->Setup(new NativeFilesystemDriver(overlayfs.lower_parent));
util::str_vec lowerDirs = {};
int prefixIndex = 0;
for (auto mount : overlayfs.mounts) {
auto prefix = util::fs::path(util::format("/%d", prefixIndex));
mount.destination = (prefix / mount.destination).string();
if (0 == nativeMounter->MountNode(mount)) {
lowerDirs.push_back((util::fs::path(overlayfs.lower_parent) / prefix).string());
}
++prefixIndex;
}
overlayfsMounter->Setup(
new OverlayfsFuseFilesystemDriver(lowerDirs, overlayfs.upper, overlayfs.workdir, hostRoot));
containerMounter = overlayfsMounter.get();
return -1;
};
auto PrepareFuseProxyRootfs = [&](const AnnotationsOverlayfs &overlayfs) -> int {
nativeMounter->Setup(new NativeFilesystemDriver(overlayfs.lower_parent));
util::str_vec mounts = {};
for (auto const &mount : overlayfs.mounts) {
auto mountItem = util::format("%s:%s\n", mount.source.c_str(), mount.destination.c_str());
mounts.push_back(mountItem);
}
fuseproxyMounter->Setup(new FuseProxyFilesystemDriver(mounts, hostRoot));
containerMounter = fuseproxyMounter.get();
return -1;
};
auto PrepareNativeRootfs = [&](const AnnotationsNativeRootfs &native) -> int {
nativeMounter->Setup(new NativeFilesystemDriver(runtime.root.path));
for (const auto &mount : native.mounts) {
nativeMounter->MountNode(mount);
}
containerMounter = nativeMounter.get();
return -1;
};
if (runtime.annotations.has_value() && runtime.annotations->overlayfs.has_value()) {
auto env = getenv("LL_BOX_FS_BACKEND");
if (env && std::string(env) == "fuse-proxy") {
return PrepareFuseProxyRootfs(runtime.annotations->overlayfs.value());
} else {
return PrepareOverlayfsRootfs(runtime.annotations->overlayfs.value());
}
} else {
return PrepareNativeRootfs(runtime.annotations->native.has_value() ? runtime.annotations->native.value()
: AnnotationsNativeRootfs());
}
return -1;
}
int MountContainerPath()
{
if (runtime.mounts.has_value()) {
for (auto const &mount : runtime.mounts.value()) {
containerMounter->MountNode(mount);
}
};
return 0;
}
};
int HookExec(const Hook &hook)
{
int execPid = fork();
if (execPid < 0) {
logErr() << "fork failed" << util::RetErrString(execPid);
return -1;
}
if (execPid == 0) {
util::str_vec argStrVec;
argStrVec.push_back(hook.path);
std::copy(hook.args->begin(), hook.args->end(), std::back_inserter(argStrVec));
util::Exec(argStrVec, hook.env.value());
exit(0);
}
return waitpid(execPid, nullptr, 0);
}
int NonePrivilegeProc(void *arg)
{
auto &containerPrivate = *reinterpret_cast<ContainerPrivate *>(arg);
if (containerPrivate.option.rootless) {
// TODO(iceyer): use option
Linux linux;
IDMap idMap;
idMap.containerID = containerPrivate.hostUid;
idMap.hostID = 0;
idMap.size = 1;
linux.uidMappings.push_back(idMap);
idMap.containerID = containerPrivate.hostGid;
idMap.hostID = 0;
idMap.size = 1;
linux.gidMappings.push_back(idMap);
ConfigUserNamespace(linux, 0);
}
auto ret = mount("proc", "/proc", "proc", 0, nullptr);
if (0 != ret) {
logErr() << "mount proc failed" << util::RetErrString(ret);
return -1;
}
if (containerPrivate.runtime.hooks.has_value() && containerPrivate.runtime.hooks->prestart.has_value()) {
for (auto const &preStart : *containerPrivate.runtime.hooks->prestart) {
HookExec(preStart);
}
}
if (!containerPrivate.option.rootless) {
seteuid(0);
// todo: check return value
ConfigSeccomp(containerPrivate.runtime.linux.seccomp);
ContainerPrivate::DropPermissions();
}
containerPrivate.forkAndExecProcess(containerPrivate.runtime.process);
containerPrivate.waitChildAndExec();
return 0;
}
void sigtermHandler(int)
{
exit(-1);
}
int EntryProc(void *arg)
{
auto &containerPrivate = *reinterpret_cast<ContainerPrivate *>(arg);
if (containerPrivate.option.rootless) {
ConfigUserNamespace(containerPrivate.runtime.linux, 0);
}
// FIXME: change HOSTNAME will broken XAUTH
auto new_hostname = containerPrivate.runtime.hostname;
// if (sethostname(new_hostname.c_str(), strlen(new_hostname.c_str())) == -1) {
// logErr() << "sethostname failed" << util::errnoString();
// return -1;
// }
uint32_t flags = MS_REC | MS_SLAVE;
int ret = mount(nullptr, "/", nullptr, flags, nullptr);
if (0 != ret) {
logErr() << "mount / failed" << util::RetErrString(ret);
return -1;
}
std::string container_root = containerPrivate.runtime.annotations->container_root_path;
flags = MS_NODEV | MS_NOSUID;
ret = mount("tmpfs", container_root.c_str(), "tmpfs", flags, nullptr);
if (0 != ret) {
logErr() << util::format("mount container root (%s) failed:", container_root.c_str())
<< util::RetErrString(ret);
return -1;
}
// NOTE(iceyer): it's not standard oci action
containerPrivate.PrepareRootfs();
containerPrivate.MountContainerPath();
if (containerPrivate.useNewCgroupNs) {
ConfigCgroupV2(containerPrivate.runtime.linux.cgroupsPath, containerPrivate.runtime.linux.resources, getpid());
}
containerPrivate.PrepareDefaultDevices();
containerPrivate.PivotRoot();
containerPrivate.PrepareLinks();
if (!containerPrivate.option.rootless) {
auto unshareFlags = 0;
// TODO(iceyer): no need user namespace in setuid
// if (c.use_delay_new_user_ns) {
// unshare_flags |= CLONE_NEWUSER;
// }
if (containerPrivate.useNewCgroupNs) {
unshareFlags |= CLONE_NEWCGROUP;
}
if (unshareFlags) {
ret = unshare(unshareFlags);
if (0 != ret) {
logErr() << "unshare failed" << unshareFlags << util::RetErrString(ret);
}
}
// if (c.use_delay_new_user_ns) {
// s.vrijgeven();
// s.passeren();
// }
}
int nonePrivilegeProcFlag = SIGCHLD | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS;
int noPrivilegePid = util::PlatformClone(NonePrivilegeProc, nonePrivilegeProcFlag, arg);
if (noPrivilegePid < 0) {
logErr() << "clone failed" << util::RetErrString(noPrivilegePid);
return -1;
}
ContainerPrivate::DropPermissions();
// FIXME: parent may dead before this return.
prctl(PR_SET_PDEATHSIG, SIGKILL);
// FIXME(interactive bash): if need keep interactive shell
containerPrivate.reader.reset();
signal(SIGTERM, sigtermHandler);
util::WaitAllUntil(noPrivilegePid);
return -1;
}
Container::Container(const Runtime &r, std::unique_ptr<util::MessageReader> reader)
: dd_ptr(new ContainerPrivate(r, std::move(reader), this))
{
}
int Container::Start(const Option &option)
{
auto &contanerPrivate = *reinterpret_cast<ContainerPrivate *>(dd_ptr.get());
contanerPrivate.option = option;
if (option.rootless) {
contanerPrivate.hostUid = geteuid();
contanerPrivate.hostGid = getegid();
}
int flags = SIGCHLD | CLONE_NEWNS;
for (auto const &n : contanerPrivate.runtime.linux.namespaces) {
switch (n.type) {
case CLONE_NEWIPC:
case CLONE_NEWUTS:
case CLONE_NEWNS:
case CLONE_NEWPID:
case CLONE_NEWNET:
flags |= n.type;
break;
case CLONE_NEWUSER:
// dd_ptr->use_delay_new_user_ns = true;
break;
case CLONE_NEWCGROUP:
contanerPrivate.useNewCgroupNs = true;
break;
default:
return -1;
}
}
if (option.rootless) {
flags |= CLONE_NEWUSER;
}
StartDbusProxy(contanerPrivate.runtime);
int entryPid = util::PlatformClone(EntryProc, flags, (void *)dd_ptr.get());
if (entryPid < 0) {
logErr() << "clone failed" << util::RetErrString(entryPid);
return -1;
}
contanerPrivate.reader.reset();
// FIXME: maybe we need c.opt.child_need_wait?
ContainerPrivate::DropPermissions();
// FIXME: parent may dead before this return.
prctl(PR_SET_PDEATHSIG, SIGKILL);
// FIXME(interactive bash): if need keep interactive shell
util::WaitAllUntil(entryPid);
return 0;
}
Container::~Container() = default;
} // namespace linglong

View File

@ -1,35 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef LINGLONG_BOX_SRC_CONTAINER_CONTAINER_H_
#define LINGLONG_BOX_SRC_CONTAINER_CONTAINER_H_
#include <memory>
#include "util/oci_runtime.h"
#include "util/message_reader.h"
namespace linglong {
struct Option;
struct ContainerPrivate;
class Container
{
public:
explicit Container(const Runtime &r, std::unique_ptr<util::MessageReader> reader);
~Container();
int Start(const Option &opt);
private:
std::unique_ptr<ContainerPrivate> dd_ptr;
};
} // namespace linglong
#endif /* LINGLONG_BOX_SRC_CONTAINER_CONTAINER_H_ */

View File

@ -1,7 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "container_option.h"

View File

@ -1,27 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef LINGLONG_BOX_SRC_CONTAINER_CONTAINER_OPTION_H_
#define LINGLONG_BOX_SRC_CONTAINER_CONTAINER_OPTION_H_
#include "util/json.h"
namespace linglong {
/*!
* It's seem not a good idea to add extra config option.
* The oci runtime json should contain option, but now it's hard to modify
* the work flow.
* If there is more information about how to deal user namespace, it can remove
*/
struct Option {
bool rootless = false;
bool linkLfs = true;
};
} // namespace linglong
#endif /* LINGLONG_BOX_SRC_CONTAINER_CONTAINER_OPTION_H_ */

View File

@ -1,182 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <sys/wait.h>
#include <utility>
#include "filesystem_driver.h"
#include "util/platform.h"
namespace linglong {
FilesystemDriver::~FilesystemDriver() = default;
OverlayfsFuseFilesystemDriver::OverlayfsFuseFilesystemDriver(util::str_vec lower_dirs, std::string upper_dir,
std::string work_dir, std::string mount_point)
: lower_dirs_(std::move(lower_dirs))
, upper_dir_(std::move(upper_dir))
, work_dir_(std::move(work_dir))
, mount_point_(std::move(mount_point))
{
}
util::fs::path OverlayfsFuseFilesystemDriver::HostPath(const util::fs::path &dest_full_path) const
{
return HostSource(util::fs::path(mount_point_) / dest_full_path);
}
int OverlayfsFuseFilesystemDriver::CreateDestinationPath(const util::fs::path &container_destination_path)
{
__mode_t dest_mode = 0755;
auto host_destination_path = HostSource(util::fs::path(mount_point_) / container_destination_path);
if (!util::fs::create_directories(host_destination_path, dest_mode)) {
logErr() << "create_directories" << host_destination_path << util::errnoString();
return -1;
}
return 0;
}
int OverlayfsFuseFilesystemDriver::Setup()
{
// fork and exec fuse
int pid = fork();
if (0 == pid) {
util::fs::create_directories(util::fs::path(work_dir_), 0755);
util::fs::create_directories(util::fs::path(upper_dir_), 0755);
util::fs::create_directories(util::fs::path(mount_point_), 0755);
// fuse-overlayfs -o lowerdir=/ -o upperdir=./upper -o workdir=./work overlaydir
util::str_vec args;
args.push_back("/usr/bin/fuse-overlayfs");
args.push_back("-o");
args.push_back("lowerdir=" + util::str_vec_join(lower_dirs_, ':'));
args.push_back("-o");
args.push_back("upperdir=" + upper_dir_);
args.push_back("-o");
args.push_back("workdir=" + work_dir_);
args.push_back(mount_point_);
logErr() << util::Exec(args, {});
logErr() << util::errnoString();
exit(0);
}
util::Wait(pid);
return 0;
}
util::fs::path OverlayfsFuseFilesystemDriver::HostSource(const util::fs::path &dest_full_path) const
{
return dest_full_path;
}
util::fs::path NativeFilesystemDriver::HostPath(const util::fs::path &dest_full_path) const
{
return util::fs::path(root_path_) / dest_full_path;
}
util::fs::path NativeFilesystemDriver::HostSource(const util::fs::path &dest_full_path) const
{
return dest_full_path;
}
int NativeFilesystemDriver::CreateDestinationPath(const util::fs::path &container_destination_path)
{
__mode_t dest_mode = 0755;
auto host_destination_path = util::fs::path(root_path_) / container_destination_path;
util::fs::create_directories(host_destination_path, dest_mode);
return 0;
}
NativeFilesystemDriver::NativeFilesystemDriver(std::string root_path)
: root_path_(std::move(root_path))
{
}
NativeFilesystemDriver::~NativeFilesystemDriver()
{
}
FuseProxyFilesystemDriver::FuseProxyFilesystemDriver(util::str_vec mounts, std::string mount_point)
: mounts_(mounts)
, mount_point_(mount_point)
{
}
util::fs::path FuseProxyFilesystemDriver::HostPath(const util::fs::path &dest_full_path) const
{
return HostSource(util::fs::path(mount_point_) / dest_full_path);
}
util::fs::path FuseProxyFilesystemDriver::HostSource(const util::fs::path &dest_full_path) const
{
return dest_full_path;
}
int FuseProxyFilesystemDriver::CreateDestinationPath(const util::fs::path &container_destination_path)
{
__mode_t dest_mode = 0755;
auto host_destination_path = HostSource(util::fs::path(mount_point_) / container_destination_path);
if (!util::fs::create_directories(host_destination_path, dest_mode)) {
logErr() << "create_directories" << host_destination_path << util::errnoString();
return -1;
}
return 0;
}
int FuseProxyFilesystemDriver::Setup()
{
int pipe_ends[2];
if (0 != pipe(pipe_ends)) {
logErr() << "pipe failed:" << util::errnoString();
return -1;
}
int pid = fork();
if (pid < 0) {
logErr() << "fork failed:" << util::errnoString();
return -1;
}
if (0 == pid) {
close(pipe_ends[1]);
if (-1 == dup2(pipe_ends[0], 112)) {
logErr() << "dup2 failed:" << util::errnoString();
return -1;
}
close(pipe_ends[0]);
util::fs::create_directories(util::fs::path(mount_point_), 0755);
util::fs::create_directories(util::fs::path(mount_point_ + "/.root"), 0755);
util::str_vec args;
args.push_back("/usr/bin/ll-fuse-proxy");
args.push_back("112");
args.push_back(mount_point_);
logErr() << util::Exec(args, {});
exit(0);
} else {
close(pipe_ends[0]);
std::string root_mount = mount_point_ + "/.root:/\n";
write(pipe_ends[1], root_mount.c_str(), root_mount.size()); // FIXME: handle write error
for (auto const &m : mounts_) {
write(pipe_ends[1], m.c_str(), m.size());
}
close(pipe_ends[1]);
return 0;
}
}
} // namespace linglong

View File

@ -1,83 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef LINGLONG_BOX_SRC_CONTAINER_MOUNT_FILESYSTEM_DRIVER_H_
#define LINGLONG_BOX_SRC_CONTAINER_MOUNT_FILESYSTEM_DRIVER_H_
#include "util/util.h"
namespace linglong {
class FilesystemDriver
{
public:
virtual int Setup() = 0;
virtual ~FilesystemDriver();
virtual int CreateDestinationPath(const util::fs::path &container_destination_path) = 0;
virtual util::fs::path HostPath(const util::fs::path &container_destination_path) const = 0;
virtual util::fs::path HostSource(const util::fs::path &container_destination_path) const = 0;
};
class OverlayfsFuseFilesystemDriver : public FilesystemDriver
{
public:
explicit OverlayfsFuseFilesystemDriver(util::str_vec lower_dirs, std::string upper_dir, std::string work_dir,
std::string mount_point);
int Setup() override;
int CreateDestinationPath(const util::fs::path &container_destination_path) override;
util::fs::path HostPath(const util::fs::path &dest_full_path) const override;
util::fs::path HostSource(const util::fs::path &dest_full_path) const override;
private:
util::str_vec lower_dirs_;
std::string upper_dir_;
std::string work_dir_;
std::string mount_point_;
};
class FuseProxyFilesystemDriver : public FilesystemDriver
{
public:
explicit FuseProxyFilesystemDriver(util::str_vec mounts, std::string mount_point);
int Setup() override;
int CreateDestinationPath(const util::fs::path &container_destination_path) override;
util::fs::path HostPath(const util::fs::path &dest_full_path) const override;
util::fs::path HostSource(const util::fs::path &dest_full_path) const override;
private:
util::str_vec mounts_;
std::string mount_point_;
};
class NativeFilesystemDriver : public FilesystemDriver
{
public:
explicit NativeFilesystemDriver(std::string root_path);
~NativeFilesystemDriver();
int Setup() override { return 0; }
int CreateDestinationPath(const util::fs::path &container_destination_path) override;
util::fs::path HostPath(const util::fs::path &dest_full_path) const override;
util::fs::path HostSource(const util::fs::path &dest_full_path) const override;
private:
std::string root_path_;
};
} // namespace linglong
#endif /* LINGLONG_BOX_SRC_CONTAINER_MOUNT_FILESYSTEM_DRIVER_H_ */

View File

@ -1,209 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "host_mount.h"
#include <sys/stat.h>
#include <utility>
#include "filesystem_driver.h"
#include "util/debug/debug.h"
namespace linglong {
class HostMountPrivate
{
public:
explicit HostMountPrivate() = default;
int CreateDestinationPath(const util::fs::path &container_destination_path) const
{
return driver_->CreateDestinationPath(container_destination_path);
}
int MountNode(const struct Mount &m) const
{
int ret = -1;
struct stat source_stat {
};
bool is_path = false;
auto source = m.source;
if (!m.source.empty() && m.source[0] == '/') {
is_path = true;
source = driver_->HostSource(util::fs::path(m.source)).string();
}
ret = lstat(source.c_str(), &source_stat);
if (0 == ret) {
} else {
// source not exist
if (m.fsType == Mount::Bind) {
logErr() << "lstat" << source << "failed";
return -1;
}
}
auto dest_full_path = util::fs::path(m.destination);
auto dest_parent_path = util::fs::path(dest_full_path).parent_path();
auto host_dest_full_path = driver_->HostPath(dest_full_path);
auto root = driver_->HostPath(util::fs::path("/"));
logDbg() << "host_dest_full_path" << host_dest_full_path;
switch (source_stat.st_mode & S_IFMT) {
case S_IFCHR: {
driver_->CreateDestinationPath(dest_parent_path);
host_dest_full_path.touch();
break;
}
case S_IFSOCK: {
driver_->CreateDestinationPath(dest_parent_path);
// FIXME: can not mound dbus socket on rootless
host_dest_full_path.touch();
break;
}
case S_IFLNK: {
driver_->CreateDestinationPath(dest_parent_path);
host_dest_full_path.touch();
source = util::fs::read_symlink(util::fs::path(source)).string();
break;
}
case S_IFREG: {
driver_->CreateDestinationPath(dest_parent_path);
host_dest_full_path.touch();
break;
}
case S_IFDIR:
driver_->CreateDestinationPath(dest_full_path);
break;
default:
driver_->CreateDestinationPath(dest_full_path);
if (is_path) {
logWan() << "unknown file type" << (source_stat.st_mode & S_IFMT) << source;
}
break;
}
auto data = util::str_vec_join(m.data, ',');
auto real_data = data;
auto real_flags = m.flags;
switch (m.fsType) {
case Mount::Bind:
// make sure m.flags always have MS_BIND
real_flags |= MS_BIND;
// When doing a bind mount, all flags expect MS_BIND and MS_REC are ignored by kernel.
real_flags &= (MS_BIND | MS_REC);
// When doing a bind mount, data and fstype are ignored by kernel. We should set them by remounting.
real_data = "";
ret = util::fs::do_mount_with_fd(root.c_str(), source.c_str(), host_dest_full_path.string().c_str(),
nullptr, real_flags, nullptr);
if (0 != ret) {
break;
}
if (source == "/sys") {
sysfs_is_binded = true;
}
if (data.empty() && (m.flags & ~(MS_BIND | MS_REC | MS_REMOUNT)) == 0) {
// no need to be remounted
break;
}
real_flags = m.flags | MS_BIND | MS_REMOUNT;
// When doing a remount, source and fstype are ignored by kernel.
real_data = data;
ret = util::fs::do_mount_with_fd(root.c_str(), nullptr, host_dest_full_path.string().c_str(), nullptr,
real_flags, real_data.c_str());
break;
case Mount::Proc:
case Mount::Devpts:
case Mount::Mqueue:
case Mount::Tmpfs:
case Mount::Sysfs:
ret = util::fs::do_mount_with_fd(root.c_str(), source.c_str(), host_dest_full_path.string().c_str(),
m.type.c_str(), real_flags, real_data.c_str());
if (ret < 0) {
// refers:
// https://github.com/containers/podman/blob/466b8991c4025006eeb43cb30e6dc990d92df72d/pkg/specgen/generate/oci.go#L178
// https://github.com/containers/crun/blob/38e1b5e2a3e9567ff188258b435085e329aaba42/src/libcrun/linux.c#L768-L789
if (m.fsType == Mount::Sysfs) {
real_flags = MS_BIND | MS_REC;
real_data = "";
ret = util::fs::do_mount_with_fd(root.c_str(), "/sys", host_dest_full_path.string().c_str(),
nullptr, real_flags, nullptr);
if (ret == 0) {
sysfs_is_binded = true;
}
} else if (m.fsType == Mount::Mqueue) {
real_flags = MS_BIND | MS_REC;
real_data = "";
ret = util::fs::do_mount_with_fd(root.c_str(), "/dev/mqueue", host_dest_full_path.string().c_str(),
nullptr, real_flags, nullptr);
}
}
break;
case Mount::Cgroup:
ret = util::fs::do_mount_with_fd(root.c_str(), source.c_str(), host_dest_full_path.string().c_str(),
m.type.c_str(), real_flags, real_data.c_str());
// When sysfs is bind-mounted, It is ok to let cgroup mount failed.
// https://github.com/containers/podman/blob/466b8991c4025006eeb43cb30e6dc990d92df72d/pkg/specgen/generate/oci.go#L281
if (sysfs_is_binded) {
ret = 0;
}
break;
default:
logErr() << "unsupported type" << m.type;
}
if (EXIT_SUCCESS != ret) {
logErr() << "mount" << source << "to" << host_dest_full_path << "failed:" << util::RetErrString(ret)
<< "\nmount args is:" << m.type << real_flags << real_data;
if (is_path) {
logErr() << "source file type is: 0x" << std::hex << (source_stat.st_mode & S_IFMT);
DUMP_FILE_INFO(source);
}
DUMP_FILE_INFO(host_dest_full_path.string());
}
return ret;
}
std::unique_ptr<FilesystemDriver> driver_;
mutable bool sysfs_is_binded = false;
};
HostMount::HostMount()
: dd_ptr(new HostMountPrivate())
{
}
int HostMount::MountNode(const struct Mount &m)
{
return dd_ptr->MountNode(m);
}
int HostMount::Setup(FilesystemDriver *driver)
{
if (nullptr == driver) {
logWan() << this << dd_ptr->driver_.get();
return 0;
}
dd_ptr->driver_ = std::unique_ptr<FilesystemDriver>(driver);
return dd_ptr->driver_->Setup();
}
HostMount::~HostMount() {};
} // namespace linglong

View File

@ -1,33 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef LINGLONG_BOX_SRC_CONTAINER_MOUNT_HOST_MOUNT_H_
#define LINGLONG_BOX_SRC_CONTAINER_MOUNT_HOST_MOUNT_H_
#include "util/oci_runtime.h"
namespace linglong {
class FilesystemDriver;
class HostMountPrivate;
class HostMount
{
public:
HostMount();
~HostMount();
int Setup(FilesystemDriver *driver);
int MountNode(const Mount &m);
private:
std::unique_ptr<HostMountPrivate> dd_ptr;
};
} // namespace linglong
#endif /* LINGLONG_BOX_SRC_CONTAINER_MOUNT_HOST_MOUNT_H_ */

View File

@ -1,143 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "seccomp.h"
#include <seccomp.h>
namespace {
#define SYSCALL_PAIR(SYSCALL) \
{ \
LL_TOSTRING(SYSCALL), SCMP_SYS(SYSCALL) \
}
std::vector<struct scmp_arg_cmp> toScmpArgCmpArray(const std::vector<linglong::SyscallArg> &args)
{
static const std::map<std::string, scmp_compare> seccompArgOpMap = {
{"_SCMP_CMP_MIN", _SCMP_CMP_MIN}, {"SCMP_CMP_NE", SCMP_CMP_NE},
{"SCMP_CMP_LT", SCMP_CMP_LT}, {"SCMP_CMP_LE", SCMP_CMP_LE},
{"SCMP_CMP_EQ", SCMP_CMP_EQ}, {"SCMP_CMP_GE", SCMP_CMP_GE},
{"SCMP_CMP_GT", SCMP_CMP_GT}, {"SCMP_CMP_MASKED_EQ", SCMP_CMP_MASKED_EQ},
{"_SCMP_CMP_MAX", _SCMP_CMP_MAX},
};
std::vector<struct scmp_arg_cmp> scmpArgs;
unsigned int index = 0;
for (auto const &arg : args) {
scmpArgs.push_back({
.arg = index,
.op = seccompArgOpMap.at(arg.op),
.datum_a = arg.value,
.datum_b = arg.valueTwo,
});
++index;
}
return scmpArgs;
}
int toSyscallNumber(const std::string &name)
{
// unistd.h for example: __NR_read
// FIXME: need full support
static const std::map<std::string, int> syscallNameMap = {
SYSCALL_PAIR(read), SYSCALL_PAIR(write), SYSCALL_PAIR(open), SYSCALL_PAIR(stat),
SYSCALL_PAIR(getcwd), SYSCALL_PAIR(chmod), SYSCALL_PAIR(syslog), SYSCALL_PAIR(uselib),
SYSCALL_PAIR(acct), SYSCALL_PAIR(modify_ldt), SYSCALL_PAIR(quotactl), SYSCALL_PAIR(add_key),
SYSCALL_PAIR(keyctl), SYSCALL_PAIR(request_key), SYSCALL_PAIR(move_pages), SYSCALL_PAIR(mbind),
SYSCALL_PAIR(get_mempolicy), SYSCALL_PAIR(set_mempolicy), SYSCALL_PAIR(migrate_pages), SYSCALL_PAIR(unshare),
SYSCALL_PAIR(mount), SYSCALL_PAIR(pivot_root), SYSCALL_PAIR(clone), SYSCALL_PAIR(ioctl),
};
return syscallNameMap.at(name);
}
} // namespace
namespace linglong {
int ConfigSeccomp(const tl::optional<linglong::Seccomp> &seccomp)
{
if (!seccomp.has_value()) {
return 0;
}
static const std::map<std::string, int> seccompActionMap = {
{"SCMP_ACT_KILL", SCMP_ACT_KILL}, {"SCMP_ACT_TRAP", SCMP_ACT_TRAP},
{"SCMP_ACT_ERRNO", SCMP_ACT_ERRNO(EPERM)}, {"SCMP_ACT_TRACE", SCMP_ACT_TRACE(EPERM)},
{"SCMP_ACT_ALLOW", SCMP_ACT_ALLOW},
};
// FIXME: need full support
static const std::map<std::string, int> seccompArchMap = {
{"SCMP_ARCH_X86", SCMP_ARCH_X86},
{"SCMP_ARCH_X86_64", SCMP_ARCH_X86_64},
{"SCMP_ARCH_X32", SCMP_ARCH_X32},
{"SCMP_ARCH_ARM", SCMP_ARCH_ARM},
{"SCMP_ARCH_AARCH64", SCMP_ARCH_AARCH64},
{"SCMP_ARCH_MIPS", SCMP_ARCH_MIPS},
{"SCMP_ARCH_MIPS64", SCMP_ARCH_MIPS64},
{"SCMP_ARCH_MIPS64N32", SCMP_ARCH_MIPS64N32},
{"SCMP_ARCH_MIPSEL", SCMP_ARCH_MIPSEL},
{"SCMP_ARCH_MIPSEL64", SCMP_ARCH_MIPSEL64},
{"SCMP_ARCH_MIPSEL64N32", SCMP_ARCH_MIPSEL64N32},
};
int ret;
scmp_filter_ctx ctx = nullptr;
try {
auto defaultAction = seccompActionMap.at(seccomp->defaultAction);
ctx = seccomp_init(defaultAction);
if (ctx == nullptr) {
throw std::runtime_error(util::errnoString() + " seccomp_init=" + seccomp->defaultAction);
}
for (auto const &architecture : seccomp->architectures) {
auto scmpArch = seccompArchMap.at(architecture);
if (seccomp_arch_exist(ctx, scmpArch) == -EEXIST) {
ret = seccomp_arch_add(ctx, scmpArch);
if (ret != 0) {
throw std::runtime_error(util::errnoString() + " architecture=" + architecture);
}
}
}
for (auto const &syscall : seccomp->syscalls) {
auto action = seccompActionMap.at(syscall.action);
auto argc = syscall.args.size();
auto args = toScmpArgCmpArray(syscall.args);
for (auto const &name : syscall.names) {
auto sysNumber = toSyscallNumber(name);
ret = seccomp_rule_add_array(ctx, action, sysNumber, argc, args.data());
if (ret != 0) {
throw std::runtime_error(util::errnoString() + " syscall.name=" + name);
}
}
}
ret = seccomp_load(ctx);
} catch (const std::exception &e) {
logErr() << "config seccomp failed:" << e.what();
ret = -1;
} catch (...) {
logErr() << "unknown error";
ret = -1;
}
if (!ctx) {
seccomp_release(ctx);
}
return ret;
}
} // namespace linglong

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef LINGLONG_BOX_SRC_CONTAINER_SECCOMP_H_
#define LINGLONG_BOX_SRC_CONTAINER_SECCOMP_H_
#include "util/oci_runtime.h"
namespace linglong {
int ConfigSeccomp(const tl::optional<linglong::Seccomp> &seccomp);
}
#endif /* LINGLONG_BOX_SRC_CONTAINER_SECCOMP_H_ */

86
src/linyaps_box/app.cpp Normal file
View File

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/app.h"
#include "linyaps_box/command/exec.h"
#include "linyaps_box/command/kill.h"
#include "linyaps_box/command/list.h"
#include "linyaps_box/command/run.h"
#include "linyaps_box/utils/log.h"
#include "utils/log.h"
namespace {
template<typename... T>
struct subCommand : T...
{
using T::operator()...;
};
template<typename... T>
subCommand(T...) -> subCommand<T...>;
} // namespace
namespace linyaps_box {
// The main function of the ll-box,
// it is the entry point.
// 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.
auto main(int argc, char **argv) noexcept -> int
try {
LINYAPS_BOX_DEBUG() << "linyaps box called with" << [=]() -> std::string {
std::stringstream result;
for (int i = 0; i < argc; ++i) {
result << " \"";
for (const auto ch : std::string_view(argv[i])) { // NOLINT
if (ch == '\\') {
result << "\\\\";
} else if (ch == '"') {
result << "\\\"";
} else {
result << ch;
}
}
result << "\"";
}
return result.str();
}();
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) -> int {
command::exec(options);
__builtin_unreachable();
},
[](const command::kill_options &options) {
command::kill(options);
return 0;
},
[](const command::run_options &options) {
return command::run(options);
},
[](const std::monostate &) {
return 0;
} },
opts.subcommand_opt);
} catch (const std::exception &e) {
LINYAPS_BOX_ERR() << "Error: " << e.what();
return -1;
} catch (...) {
LINYAPS_BOX_ERR() << "unknown error";
return -1;
}
} // namespace linyaps_box

11
src/linyaps_box/app.h Normal file
View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
namespace linyaps_box {
auto main(int argc, char **argv) noexcept -> int;
} // namespace linyaps_box

61
src/linyaps_box/cgroup.h Normal file
View File

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <filesystem>
#include <string>
#include <unordered_map>
namespace linyaps_box {
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::filesystem::path state_root;
std::string id;
pid_t pid;
// resources and so on...
};
struct cgroup_status
{
public:
[[nodiscard]] auto path() const noexcept -> std::filesystem::path { return path_; }
[[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;
std::filesystem::path path_;
std::string scope_;
cgroup_manager_t manager_;
};
inline auto operator<<(std::ostream &stream, cgroup_manager_t manager) -> std::ostream &
{
switch (manager) {
case cgroup_manager_t::disabled: {
stream << "disabled";
} break;
case cgroup_manager_t::systemd: {
stream << "systemd";
} break;
case cgroup_manager_t::cgroupfs: {
stream << "cgroupfs";
} break;
default: {
stream << "unknown";
} break;
}
return stream;
}
} // namespace linyaps_box

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <linyaps_box/cgroup_manager.h>
namespace linyaps_box {
cgroup_manager::~cgroup_manager() = default;
} // namespace linyaps_box

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/utils/file_describer.h"
#include <linyaps_box/cgroup.h>
#include <linyaps_box/interface.h>
namespace linyaps_box {
class cgroup_manager : public virtual interface
{
public:
cgroup_manager() = default;
~cgroup_manager() override;
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;
virtual void destroy_cgroup(const cgroup_status &status) = 0;
// TODO: support update resource
// virtual void update_resource(const cgroup_status &status, ) = 0;
protected:
static void set_manager(cgroup_status &status, cgroup_manager_t type) noexcept
{
status.manager_ = type;
}
};
} // namespace linyaps_box

View File

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/command/exec.h"
#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/runtime.h"
#include "linyaps_box/status_directory.h"
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);
runtime_t runtime(std::move(dir));
auto container_refs = runtime.containers();
auto container = container_refs.find(options.ID);
if (container == container_refs.end()) {
throw std::runtime_error("container not found");
}
config::process_t proc;
proc.cwd = options.cwd.value_or("/");
proc.args = options.command;
proc.terminal = isatty(STDIN_FILENO) == 1 && isatty(STDOUT_FILENO) == 1;
proc.no_new_privileges = options.no_new_privs;
proc.env = options.envs.value_or(std::vector<std::string>{});
#ifdef LINYAPS_BOX_ENABLE_CAP
if (options.caps) {
const auto &caps = options.caps.value();
auto transform_cap = [&caps](std::vector<cap_value_t> &cap_set) {
std::transform(caps.cbegin(),
caps.cend(),
std::back_inserter(cap_set),
[](const std::string &cap) {
cap_value_t val{ 0 };
if (cap_from_name(cap.c_str(), &val) < 0) {
throw std::system_error(errno,
std::generic_category(),
"cap_from_name");
}
return val;
});
};
transform_cap(proc.capabilities.effective);
transform_cap(proc.capabilities.ambient);
transform_cap(proc.capabilities.bounding);
transform_cap(proc.capabilities.permitted);
}
#endif
// TODO: support exec fully
container->second.exec(proc);
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/command/options.h"
namespace linyaps_box::command {
[[noreturn]] void exec(const exec_options &options);
} // namespace linyaps_box::command

View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/command/kill.h"
#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/runtime.h"
#include "linyaps_box/utils/platform.h"
#include <algorithm>
void linyaps_box::command::kill(const struct kill_options &options)
{
auto signal{ options.signal };
int sig{ -1 };
while (true) {
if (std::all_of(signal.cbegin(), signal.cend(), ::isdigit)) {
sig = std::stoi(signal);
break;
}
if (signal.rfind("SIG", 0) == std::string::npos) {
signal.insert(0, "SIG");
}
sig = utils::str_to_signal(signal);
break;
}
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");
}
runtime_t runtime(std::move(status_dir));
const auto &containers = runtime.containers();
for (const auto &[id, ref] : containers) {
if (id != options.container) {
continue;
}
ref.kill(sig);
return;
}
throw std::runtime_error("container not found");
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/command/options.h"
namespace linyaps_box::command {
void kill(const kill_options &options);
} // namespace linyaps_box::command

View File

@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/command/list.h"
#include "linyaps_box/impl/json_printer.h"
#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/impl/table_printer.h"
#include "linyaps_box/runtime.h"
#include <memory>
void linyaps_box::command::list(const struct list_options &options)
{
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");
}
runtime_t runtime(std::move(status_dir));
std::unique_ptr<printer> printer;
if (options.output_format == list_options::output_format_t::json) {
printer = std::make_unique<impl::json_printer>();
} else {
printer = std::make_unique<impl::table_printer>();
}
auto containers = runtime.containers();
std::vector<container_status_t> statuses;
statuses.reserve(containers.size());
for (const auto &[_, container] : containers) {
statuses.emplace_back(container.status());
}
printer->print_statuses(statuses);
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/command/options.h"
namespace linyaps_box::command {
void list(const list_options &options);
} // namespace linyaps_box::command

View File

@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/command/options.h"
#include "linyaps_box/config.h"
#include "linyaps_box/version.h"
#include <CLI/CLI.hpp>
#include <unistd.h>
linyaps_box::command::options linyaps_box::command::parse(int argc, char *argv[]) // NOLINT
{
CLI::App app{ "A simple OCI runtime implementation focused on desktop applications.",
"ll-box" };
app.set_version_flag("-v,--version", [] {
std::stringstream ss;
ss << "ll-box version " << LINYAPS_BOX_VERSION << "\n";
ss << "spec " << linyaps_box::config::oci_version;
return ss.str();
});
app.require_subcommand();
linyaps_box::command::options options;
auto default_root = std::filesystem::current_path().root_path() / "run" / "user"
/ std::to_string(geteuid()) / "linglong" / "box";
if (auto *env = getenv("XDG_RUNTIME_DIR"); env != nullptr) {
default_root = std::filesystem::path{ env } / "linglong" / "box";
}
app.add_option("--root", options.global.root, "Root directory for storage of container state")
->default_val(default_root);
app.add_option("--cgroup-manager", options.global.manager, "Cgroup manager to use")
->type_name("MANAGER")
->transform(
CLI::CheckedTransformer(std::unordered_map<std::string_view, cgroup_manager_t>{
{ "cgroupfs", cgroup_manager_t::cgroupfs },
{ "systemd", cgroup_manager_t::systemd },
{ "disabled", cgroup_manager_t::disabled },
}))
->default_val(cgroup_manager_t::disabled);
list_options list_opt{ options.global };
auto *cmd_list = app.add_subcommand("list", "List know containers");
cmd_list->add_option("-f,--format", list_opt.output_format, "Specify the output format")
->type_name("FORMAT")
->transform(CLI::CheckedTransformer(
std::unordered_map<std::string_view, list_options::output_format_t>{
{ "json", list_options::output_format_t::json },
{ "table", list_options::output_format_t::table },
}))
->default_val(list_options::output_format_t::table);
run_options run_opt{ options.global };
auto *cmd_run = app.add_subcommand("run", "Create and immediately start a container");
cmd_run->add_option("CONTAINER", run_opt.ID, "The container ID")->required();
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")
->positionals_at_end();
cmd_exec->add_option("-u,--user",
exec_opt.user,
"Specify the user, "
"for example `1000` for UID=1000 "
"or `1000:1000` for UID=1000 and GID=1000")
->type_name("UID[:GID]");
cmd_exec->add_option("--cwd", exec_opt.cwd, "Current working directory.");
cmd_exec->add_option("--env", exec_opt.envs, "Environment variables to set")
->multi_option_policy(CLI::MultiOptionPolicy::TakeAll)
->check(
[](const std::string &str) {
if (str.find('=') == std::string::npos) {
return "invalid argument, env must be in the format of KEY=VALUE";
}
return "";
},
"env_check");
// TODO: enable capabilities and no_new_privs support after rewrite exec,
// cmd_exec->add_option("-c,--cap", options.exec.caps, "Set capabilities")
// ->check(
// [](const std::string &val) -> std::string {
// if (val.rfind("CAP_", 0) != std::string::npos) {
// return "";
// }
// return "invalid argument, capability must start with CAP_";
// },
// "cap_check");
// cmd_exec->add_flag("--no-new-privs",
// options.exec.no_new_privs,
// "Set the no new privileges value for the process")
// ->default_val(false);
cmd_exec->add_option("CONTAINER", exec_opt.ID, "Container ID")->required();
cmd_exec->add_option("COMMAND", exec_opt.command, "Command to execute")->required();
kill_options kill_opt{ options.global };
auto *cmd_kill =
app.add_subcommand("kill", "Send the specified signal to the container init process");
cmd_kill->add_option("CONTAINER", kill_opt.container, "The container ID")->required();
cmd_kill->add_option("SIGNAL", kill_opt.signal, "Signal to send")->default_val("SIGTERM");
argv = app.ensure_utf8(argv);
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
options.global.return_code = app.exit(e);
return options;
}
if (cmd_list->parsed()) {
options.subcommand_opt = list_opt;
} else if (cmd_run->parsed()) {
options.subcommand_opt = run_opt;
} else if (cmd_exec->parsed()) {
options.subcommand_opt = exec_opt;
} else if (cmd_kill->parsed()) {
options.subcommand_opt = kill_opt;
}
return options;
}

View File

@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <linyaps_box/cgroup_manager.h>
#include <filesystem>
#include <optional>
#include <variant>
#include <vector>
namespace linyaps_box::command {
struct global_options
{
cgroup_manager_t manager{ cgroup_manager_t::disabled };
std::filesystem::path root;
int return_code{ 0 };
};
struct list_options
{
enum class output_format_t : std::uint8_t { table, json };
explicit list_options(global_options &global)
: global_(global)
{
}
output_format_t output_format{ output_format_t::table };
std::reference_wrapper<global_options> global_;
};
struct exec_options
{
explicit exec_options(global_options &global)
: no_new_privs(false)
, global_(global)
{
}
bool no_new_privs;
std::reference_wrapper<global_options> global_;
std::vector<std::string> command;
std::string user;
std::optional<std::vector<std::string>> caps;
std::string ID;
std::optional<std::string> cwd;
std::optional<std::vector<std::string>> envs;
};
struct run_options
{
explicit run_options(global_options &global)
: global_(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)
{
}
std::reference_wrapper<global_options> global_;
std::string container;
std::string signal;
};
struct options
{
options()
: global()
{
}
using subcommand_opt_t =
std::variant<std::monostate, list_options, exec_options, run_options, kill_options>;
global_options global;
subcommand_opt_t subcommand_opt;
};
// This function parses the command line arguments.
// It might print help or usage to stdout or stderr.
options parse(int argc, char *argv[]); // NOLINT
} // namespace linyaps_box::command

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/command/run.h"
#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/runtime.h"
#include "linyaps_box/status_directory.h"
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);
runtime_t runtime(std::move(dir));
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);
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/command/options.h"
namespace linyaps_box::command {
[[nodiscard]] auto run(const run_options &options) -> int;
} // namespace linyaps_box::command

467
src/linyaps_box/config.cpp Normal file
View File

@ -0,0 +1,467 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/config.h"
#include "linyaps_box/utils/semver.h"
#include "nlohmann/json.hpp"
#include <sys/resource.h>
namespace {
// 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 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 std::unordered_map<std::string_view, unsigned long> flags_map{
{ "bind", MS_BIND },
{ "defaults", 0 },
{ "dirsync", MS_DIRSYNC },
{ "iversion", MS_I_VERSION },
{ "lazytime", MS_LAZYTIME },
{ "mand", MS_MANDLOCK },
{ "noatime", MS_NOATIME },
{ "nodev", MS_NODEV },
{ "nodiratime", MS_NODIRATIME },
{ "noexec", MS_NOEXEC },
{ "nosuid", MS_NOSUID },
{ "nosymfollow", LINGYAPS_MS_NOSYMFOLLOW },
{ "rbind", MS_BIND | MS_REC },
{ "relatime", MS_RELATIME },
{ "remount", MS_REMOUNT },
{ "ro", MS_RDONLY },
{ "silent", MS_SILENT },
{ "strictatime", MS_STRICTATIME },
{ "sync", MS_SYNCHRONOUS },
};
const std::unordered_map<std::string_view, unsigned long> unset_flags_map{
{ "async", MS_SYNCHRONOUS },
{ "atime", MS_NOATIME },
{ "dev", MS_NODEV },
{ "diratime", MS_NODIRATIME },
{ "exec", MS_NOEXEC },
{ "loud", MS_SILENT },
{ "noiversion", MS_I_VERSION },
{ "nolazytime", MS_LAZYTIME },
{ "nomand", MS_MANDLOCK },
{ "norelatime", MS_RELATIME },
{ "nostrictatime", MS_STRICTATIME },
{ "rw", MS_RDONLY },
{ "suid", MS_NOSUID },
{ "symfollow", LINGYAPS_MS_NOSYMFOLLOW },
};
const std::unordered_map<std::string_view, std::uint8_t> extra_flags_map{
{ "copy-symlink", linyaps_box::config::mount_t::COPY_SYMLINK }
};
unsigned long flags = 0;
std::uint8_t extra_flags = 0;
unsigned long propagation_flags = 0;
std::stringstream data;
for (const auto &opt : options) {
if (auto it = flags_map.find(opt); it != flags_map.end()) {
flags |= it->second;
continue;
}
if (auto it = unset_flags_map.find(opt); it != unset_flags_map.end()) {
flags &= ~it->second;
continue;
}
if (auto it = propagation_flags_map.find(opt); it != propagation_flags_map.end()) {
propagation_flags |= it->second;
continue;
}
if (auto it = extra_flags_map.find(opt); it != extra_flags_map.end()) {
extra_flags |= it->second;
continue;
}
data << "," << opt;
}
auto str = data.str();
if (!str.empty()) {
str = str.substr(1);
}
return { flags, propagation_flags, extra_flags, str };
}
#ifdef LINYAPS_BOX_ENABLE_CAP
linyaps_box::config::process_t::capabilities_t
parse_capability(const nlohmann::json &obj, const nlohmann::json::json_pointer &ptr)
{
auto parse_cap_set = [&obj, &ptr](const char *set_name) {
const auto set = ptr / set_name;
std::vector<cap_value_t> cap_list;
if (!obj.contains(set)) {
return cap_list;
}
const auto vec = obj[set].get<std::vector<std::string>>();
std::for_each(vec.cbegin(), vec.cend(), [&cap_list](const std::string &cap) {
cap_value_t val{ 0 };
if (cap_from_name(cap.c_str(), &val) < 0) {
throw std::runtime_error("unknown capability: " + cap);
}
cap_list.push_back(val);
});
return cap_list;
};
linyaps_box::config::process_t::capabilities_t cap{};
cap.effective = parse_cap_set("effective");
cap.ambient = parse_cap_set("ambient");
cap.bounding = parse_cap_set("bounding");
cap.inheritable = parse_cap_set("inheritable");
cap.permitted = parse_cap_set("permitted");
return cap;
}
#endif
linyaps_box::config::process_t::rlimits_t parse_rlimits(const nlohmann::json &obj,
const nlohmann::json::json_pointer &ptr)
{
const auto &vec = obj[ptr];
if (!vec.is_array()) {
throw std::runtime_error("rlimits must be an array");
}
linyaps_box::config::process_t::rlimits_t ret{};
std::transform(
vec.cbegin(),
vec.cend(),
std::back_inserter(ret),
[](const nlohmann::json &json) {
if (!json.is_object()) {
throw std::runtime_error("rlimit must be an object");
}
if (!json.contains("type")) {
throw std::runtime_error("rlimit must contain type");
}
return linyaps_box::config::process_t::rlimit_t{ json["type"].get<std::string>(),
json["soft"].get<uint64_t>(),
json["hard"].get<uint64_t>() };
});
return ret;
}
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)) {
const auto &vec = obj[uid_ptr];
std::vector<linyaps_box::config::linux_t::id_mapping_t> uid_mappings;
std::transform(vec.cbegin(),
vec.cend(),
std::back_inserter(uid_mappings),
[](const nlohmann::json &json) {
return linyaps_box::config::linux_t::id_mapping_t{
json["hostID"].get<uid_t>(),
json["containerID"].get<uid_t>(),
json["size"].get<size_t>(),
};
});
linux.uid_mappings = std::move(uid_mappings);
}
if (auto gid_ptr = ptr / "gidMappings"; obj.contains(gid_ptr)) {
const auto &vec = obj[gid_ptr];
std::vector<linyaps_box::config::linux_t::id_mapping_t> gid_mappings;
std::transform(vec.cbegin(),
vec.cend(),
std::back_inserter(gid_mappings),
[](const nlohmann::json &json) {
return linyaps_box::config::linux_t::id_mapping_t{
json["hostID"].get<uid_t>(),
json["containerID"].get<uid_t>(),
json["size"].get<size_t>(),
};
});
linux.gid_mappings = std::move(gid_mappings);
}
if (auto namespace_ptr = ptr / "namespaces"; obj.contains(namespace_ptr)) {
auto map_fn = [](const nlohmann::json &json) {
if (!json.contains("type")) {
throw std::runtime_error("property `type` is REQUIRED for linux namespaces");
}
linyaps_box::config::linux_t::namespace_t n;
auto type = json["type"].get<std::string>();
if (type == "pid") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::PID;
} else if (type == "network") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::NET;
} else if (type == "ipc") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::IPC;
} else if (type == "uts") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::UTS;
} else if (type == "mount") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::MOUNT;
} else if (type == "user") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::USER;
} else if (type == "cgroup") {
n.type = linyaps_box::config::linux_t::namespace_t::type_t::CGROUP;
} else {
throw std::runtime_error("unsupported namespace type: " + type);
}
if (json.contains("path")) {
n.path = json["path"].get<std::string>();
}
return n;
};
const auto &vec = obj[namespace_ptr];
std::vector<linyaps_box::config::linux_t::namespace_t> ns;
std::transform(vec.cbegin(), vec.cend(), std::back_inserter(ns), map_fn);
linux.namespaces = std::move(ns);
}
if (auto masked_path = ptr / "maskedPaths"; obj.contains(masked_path)) {
const auto &vec = obj[masked_path];
std::vector<std::filesystem::path> masked_paths;
std::transform(vec.cbegin(),
vec.cend(),
std::back_inserter(masked_paths),
[](const nlohmann::json &json) {
return json.get<std::string>();
});
linux.masked_paths = std::move(masked_paths);
}
if (auto readonly_path = ptr / "readonlyPaths"; obj.contains(readonly_path)) {
const auto &vec = obj[readonly_path];
std::vector<std::filesystem::path> readonly_paths;
std::transform(vec.cbegin(),
vec.cend(),
std::back_inserter(readonly_paths),
[](const nlohmann::json &json) {
return json.get<std::string>();
});
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;
}
auto parse_1_2_0(const nlohmann::json &j) -> linyaps_box::config
{
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)) {
throw std::runtime_error("unsupported OCI version: " + semver.to_string());
}
linyaps_box::config cfg;
{
if (j.contains(ptr / "process" / "terminal")) {
cfg.process.terminal = j[ptr / "process" / "terminal"].get<bool>();
}
// https://github.com/opencontainers/runtime-spec/blob/09fcb39bb7185b46dfb206bc8f3fea914c674779/config.md?plain=1#L245
if (cfg.process.terminal && j.contains(ptr / "process" / "consoleSize")) {
cfg.process.console.height = j[ptr / "process" / "consoleSize" / "height"].get<uint>();
cfg.process.console.width = j[ptr / "process" / "consoleSize" / "width"].get<uint>();
}
cfg.process.cwd = j[ptr / "process" / "cwd"].get<std::string>();
if (j.contains(ptr / "process" / "env")) {
auto env = j[ptr / "process" / "env"].get<std::vector<std::string>>();
for (const auto &e : env) {
auto pos = e.find('=');
if (pos == std::string::npos) {
throw std::runtime_error("invalid env entry: " + e);
}
}
cfg.process.env = std::move(env);
}
cfg.process.args = j[ptr / "process" / "args"].get<std::vector<std::string>>();
if (auto rlimits = ptr / "process" / "rlimits"; j.contains(rlimits)) {
cfg.process.rlimits = parse_rlimits(j, rlimits);
}
// TODO: apparmorProfile
#ifdef LINYAPS_BOX_ENABLE_CAP
if (auto cap = ptr / "process" / "capabilities"; j.contains(cap)) {
cfg.process.capabilities = parse_capability(j, cap);
}
#endif
if (j.contains(ptr / "process" / "noNewPrivileges")) {
cfg.process.no_new_privileges = j[ptr / "process" / "noNewPrivileges"].get<bool>();
}
if (j.contains(ptr / "process" / "oomScoreAdj")) {
cfg.process.oom_score_adj = j[ptr / "process" / "oomScoreAdj"].get<int>();
}
cfg.process.user.uid = j[ptr / "process" / "user" / "uid"].get<uid_t>();
cfg.process.user.gid = j[ptr / "process" / "user" / "gid"].get<gid_t>();
if (j.contains(ptr / "process" / "user" / "umask")) {
cfg.process.user.umask = j[ptr / "process" / "user" / "umask"].get<mode_t>();
}
if (j.contains(ptr / "process" / "user" / "additionalGids")) {
cfg.process.user.additional_gids =
j[ptr / "process" / "user" / "additionalGids"].get<std::vector<gid_t>>();
}
}
if (auto linux_ptr = ptr / "linux"; j.contains(linux_ptr)) {
cfg.linux = parse_linux(j, linux_ptr);
}
if (j.contains(ptr / "hooks")) {
auto hooks = j[ptr / "hooks"];
auto get_hooks = [&](const std::string &key)
-> std::optional<std::vector<linyaps_box::config::hooks_t::hook_t>> {
if (!hooks.contains(key)) {
return std::nullopt;
}
std::vector<linyaps_box::config::hooks_t::hook_t> result;
for (const auto &h : hooks[key]) {
linyaps_box::config::hooks_t::hook_t hook;
hook.path = h["path"].get<std::string>();
if (!hook.path.is_absolute()) {
throw std::runtime_error(key + "path must be absolute");
}
if (h.contains("args")) {
hook.args = h["args"].get<std::vector<std::string>>();
}
if (h.contains("env")) {
std::unordered_map<std::string, std::string> env;
for (const auto &e : h["env"].get<std::vector<std::string>>()) {
auto pos = e.find('=');
if (pos == std::string::npos) {
throw std::runtime_error("invalid env entry: " + e);
}
env[e.substr(0, pos)] = e.substr(pos + 1);
}
hook.env = std::move(env);
}
if (h.contains("timeout")) {
hook.timeout = h["timeout"].get<int>();
if (hook.timeout <= 0) {
throw std::runtime_error(key + "timeout must be greater than zero");
}
}
result.push_back(hook);
}
return result;
};
cfg.hooks.prestart = get_hooks("prestart");
cfg.hooks.create_runtime = get_hooks("createRuntime");
cfg.hooks.create_container = get_hooks("createContainer");
cfg.hooks.start_container = get_hooks("startContainer");
cfg.hooks.poststart = get_hooks("poststart");
cfg.hooks.poststop = get_hooks("poststop");
}
if (j.contains(ptr / "mounts")) {
std::vector<linyaps_box::config::mount_t> mounts;
for (const auto &m : j[ptr / "mounts"]) {
linyaps_box::config::mount_t mount;
if (m.contains("source")) {
mount.source = m["source"].get<std::string>();
}
if (m.contains("destination")) {
mount.destination = m["destination"].get<std::string>();
}
mount.type = m["type"].get<std::string>();
const auto it = m.find("options");
if (it != m.end()) {
auto options = it->get<std::vector<std::string>>();
std::tie(mount.flags, mount.propagation_flags, mount.extra_flags, mount.data) =
parse_mount_options(options);
}
mounts.push_back(mount);
}
cfg.mounts = mounts;
}
auto root = ptr / "root";
if (!j.contains(root)) {
throw std::runtime_error("root must be specified");
}
if (!j.contains(root / "path")) {
throw std::runtime_error("root.path must be specified");
}
cfg.root.path = j[root / "path"].get<std::filesystem::path>();
if (j.contains(root / "readonly")) {
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;
}
} // namespace
linyaps_box::config linyaps_box::config::parse(std::istream &is)
{
auto j = nlohmann::json::parse(is);
return parse_1_2_0(j);
}

177
src/linyaps_box/config.h Normal file
View File

@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <sys/mount.h>
#include <filesystem>
#include <optional>
#include <unordered_map>
#include <vector>
#include <sys/resource.h>
#include <sys/types.h>
#ifdef LINYAPS_BOX_ENABLE_CAP
#include <sys/capability.h>
#endif
// Compatible with linux kernel which is under 5.10
#ifndef MS_NOSYMFOLLOW
#define LINGYAPS_MS_NOSYMFOLLOW 256
#else
#define LINGYAPS_MS_NOSYMFOLLOW MS_NOSYMFOLLOW
#endif
namespace linyaps_box {
struct config
{
static constexpr auto oci_version = "1.2.0";
static auto parse(std::istream &is) -> config;
struct process_t
{
bool terminal = false;
struct console_t
{
uint height = 0;
uint width = 0;
};
console_t console;
std::filesystem::path cwd;
std::vector<std::string> env;
std::vector<std::string> args;
struct rlimit_t
{
// use string for runtime log
std::string type;
uint64_t soft;
uint64_t hard;
};
using rlimits_t = std::vector<rlimit_t>;
std::optional<rlimits_t> rlimits;
std::optional<std::string> apparmor_profile;
// TODO: not use cap_value_t directly
#ifdef LINYAPS_BOX_ENABLE_CAP
struct capabilities_t
{
std::vector<cap_value_t> effective;
std::vector<cap_value_t> bounding;
std::vector<cap_value_t> inheritable;
std::vector<cap_value_t> permitted;
std::vector<cap_value_t> ambient;
};
capabilities_t capabilities;
#endif
bool no_new_privileges = false;
std::optional<int> oom_score_adj;
struct user_t
{
uid_t uid;
gid_t gid;
std::optional<mode_t> umask;
std::optional<std::vector<gid_t>> additional_gids;
};
user_t user;
};
process_t process;
struct linux_t
{
struct id_mapping_t
{
uid_t host_id;
uid_t container_id;
size_t size;
};
struct namespace_t
{
enum type_t {
INVALID = 0,
IPC = CLONE_NEWIPC,
UTS = CLONE_NEWUTS,
MOUNT = CLONE_NEWNS,
PID = CLONE_NEWPID,
NET = CLONE_NEWNET,
USER = CLONE_NEWUSER,
CGROUP = CLONE_NEWCGROUP,
};
type_t type{ type_t::INVALID };
std::optional<std::filesystem::path> path;
};
std::optional<std::vector<namespace_t>> namespaces;
std::optional<std::vector<id_mapping_t>> uid_mappings;
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;
struct hooks_t
{
struct hook_t
{
std::filesystem::path path;
std::optional<std::vector<std::string>> args;
std::optional<std::unordered_map<std::string, std::string>> env;
std::optional<int> timeout;
};
std::optional<std::vector<hook_t>> prestart;
std::optional<std::vector<hook_t>> create_runtime;
std::optional<std::vector<hook_t>> create_container;
std::optional<std::vector<hook_t>> start_container;
std::optional<std::vector<hook_t>> poststart;
std::optional<std::vector<hook_t>> poststop;
};
hooks_t hooks;
struct mount_t
{
enum extension : std::uint8_t { COPY_SYMLINK = 1 };
std::optional<std::string> source;
std::optional<std::filesystem::path> destination;
std::string type;
std::uint8_t extra_flags{ 0 };
unsigned long flags{ 0 };
unsigned long propagation_flags{ 0 };
std::string data;
};
std::vector<mount_t> mounts;
struct root_t
{
std::filesystem::path path;
bool readonly{ false };
};
root_t root;
std::optional<std::unordered_map<std::string, std::string>> annotations;
};
} // namespace linyaps_box

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/cgroup_manager.h"
#include "linyaps_box/container_ref.h"
#include "linyaps_box/status_directory.h"
#include "linyaps_box/utils/file_describer.h"
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 create_container_options_t &options);
container(const container &) = delete;
auto operator=(const container &) -> container & = delete;
container(container &&) = delete;
auto operator=(container &&) -> container & = delete;
[[nodiscard]] auto get_config() const -> const linyaps_box::config &;
[[nodiscard]] auto get_bundle() const -> const std::filesystem::path &;
[[nodiscard]] auto run(const config::process_t &process) const -> int;
// TODO:: support fully container capabilities, e.g. create, start, stop, delete...
friend 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_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);
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

View File

@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/container_ref.h"
#include "linyaps_box/utils/log.h"
#include <csignal> // IWYU pragma: keep
#include <utility>
#include <unistd.h>
linyaps_box::container_ref::container_ref(const status_directory &status_dir, std::string 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_);
}
void linyaps_box::container_ref::kill(int signal) const
{
auto pid = this->status().PID;
LINYAPS_BOX_DEBUG() << "kill process " << pid << " with signal " << signal;
if (::kill(pid, signal) == 0) {
return;
}
std::stringstream ss;
ss << "failed to kill process " << pid << " with signal " << signal;
throw std::system_error(errno, std::generic_category(), std::move(ss).str());
}
void linyaps_box::container_ref::exec(const linyaps_box::config::process_t &process)
{
auto target = std::to_string(this->status().PID);
std::vector<const char *> argv{
"nsenter",
"--target",
target.c_str(),
"--user",
"--mount",
"--pid",
// FIXME:
// Old nsenter command do not support --wdns,
// so we have to implement nsenter by ourself in the future.
"--preserve-credentials",
};
for (const auto &arg : process.args) {
argv.push_back(arg.c_str());
}
argv.push_back(nullptr);
std::vector<const char *> c_env;
c_env.reserve(process.env.size());
for (const auto &env : process.env) {
c_env.push_back(env.c_str());
}
c_env.push_back(nullptr);
LINYAPS_BOX_DEBUG() << [&argv]() -> std::string {
auto result = std::accumulate(argv.cbegin(),
argv.cend() - 1,
std::string{ "args:[" },
[](std::string init, const std::string &val) {
init += val;
init.push_back(' ');
return init;
});
result.push_back(']');
result.insert(0, "execvp nsenter with arguments: ");
return result;
}();
// FIXME:
// We only handle the command arguments for now
// here are some other fields in process we need to consider:
// terminal
// console.height
// console.width
// cwd
// env
// rlimits
// apparmor_profile
// capabilities
// no_new_privileges
// oom_score_adj
::execvpe("nsenter", const_cast<char **>(argv.data()), const_cast<char **>(c_env.data()));
std::stringstream ss;
ss << "execvp nsenter with arguments:";
for (const auto &arg : argv) {
ss << " " << arg;
}
throw std::system_error(errno, std::generic_category(), std::move(ss).str());
}
const linyaps_box::status_directory &linyaps_box::container_ref::status_dir() const
{
return this->status_dir_;
}
const std::string &linyaps_box::container_ref::get_id() const
{
return this->id_;
}

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/config.h"
#include "linyaps_box/container_status.h"
#include "linyaps_box/status_directory.h"
namespace linyaps_box {
class container_ref
{
public:
container_ref(const status_directory &status_dir, std::string id);
virtual ~container_ref() noexcept;
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]] auto status_dir() const -> const status_directory &;
[[nodiscard]] auto get_id() const -> const std::string &;
private:
std::string id_;
const status_directory &status_dir_;
};
} // namespace linyaps_box

View File

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/container_status.h"
namespace linyaps_box {
auto to_string(linyaps_box::container_status_t::runtime_status status) -> std::string
{
switch (status) {
case linyaps_box::container_status_t::runtime_status::CREATING:
return "creating";
case linyaps_box::container_status_t::runtime_status::CREATED:
return "created";
case linyaps_box::container_status_t::runtime_status::RUNNING:
return "running";
case linyaps_box::container_status_t::runtime_status::STOPPED:
return "stopped";
default:
throw std::logic_error("unknown 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;
}
if (status == "created") {
return linyaps_box::container_status_t::runtime_status::CREATED;
}
if (status == "running") {
return linyaps_box::container_status_t::runtime_status::RUNNING;
}
if (status == "stopped") {
return linyaps_box::container_status_t::runtime_status::STOPPED;
}
throw std::logic_error("unknown status");
}
auto status_to_json(const linyaps_box::container_status_t &status) -> nlohmann::json
{
return nlohmann::json::object({ { "id", status.ID },
{ "pid", status.PID },
{ "status", to_string(status.status) },
{ "bundle", status.bundle.string() },
{ "created", status.created },
{ "owner", status.owner },
{ "annotations", status.annotations },
{ "ociVersion", status.oci_version } });
}
} // namespace linyaps_box

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <nlohmann/json.hpp>
#include <filesystem>
#include <string>
#include <unordered_map>
namespace linyaps_box {
struct container_status_t
{
std::string ID;
pid_t PID;
std::string oci_version;
enum class runtime_status : std::uint8_t { CREATING, CREATED, RUNNING, STOPPED };
runtime_status status;
std::filesystem::path bundle;
std::string created; // extension field
std::string owner; // extension field
std::unordered_map<std::string, std::string> annotations;
};
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

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/impl/disabled_cgroup_manager.h"
namespace linyaps_box {
[[nodiscard]] auto disabled_cgroup_manager::type() const -> cgroup_manager_t
{
return cgroup_manager_t::disabled;
}
auto disabled_cgroup_manager::create_cgroup([[maybe_unused]] const cgroup_options &options)
-> cgroup_status
{
cgroup_status status{};
set_manager(status, type());
return status;
}
void disabled_cgroup_manager::precreate_cgroup([[maybe_unused]] const cgroup_options &options,
[[maybe_unused]] utils::file_descriptor &dirfd)
{
}
void disabled_cgroup_manager::destroy_cgroup([[maybe_unused]] const cgroup_status &status) { }
} // namespace linyaps_box

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <linyaps_box/cgroup_manager.h>
namespace linyaps_box {
class disabled_cgroup_manager : public virtual cgroup_manager
{
public:
[[nodiscard]] auto type() const -> cgroup_manager_t 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;
void destroy_cgroup([[maybe_unused]] const cgroup_status &status) override;
};
} // namespace linyaps_box

View File

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/impl/json_printer.h"
#include "nlohmann/json.hpp"
#include <iostream>
void linyaps_box::impl::json_printer::print_statuses(const std::vector<container_status_t> &status)
{
auto j = nlohmann::json::array();
for (const auto &s : status) {
j += status_to_json(s);
}
std::cout << j.dump(4) << std::endl;
}
void linyaps_box::impl::json_printer::print_status(const container_status_t &status)
{
auto j = status_to_json(status);
std::cout << j.dump(4) << std::endl;
}

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/container_status.h"
#include "linyaps_box/printer.h"
namespace linyaps_box::impl {
class json_printer final : public virtual linyaps_box::printer
{
public:
void print_status(const container_status_t &status) final;
void print_statuses(const std::vector<container_status_t> &status) final;
};
static_assert(!std::is_abstract_v<json_printer>);
} // namespace linyaps_box::impl

View File

@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/container_status.h"
#include "linyaps_box/utils/atomic_write.h"
#include "linyaps_box/utils/log.h"
#include "nlohmann/json.hpp"
#include <csignal> // IWYU pragma: keep
#include <fstream>
#include <utility>
#include <unistd.h>
namespace {
auto read_status(const std::filesystem::path &path) -> linyaps_box::container_status_t
{
nlohmann::json j;
{
std::ifstream istrm(path);
if (istrm.fail()) {
throw std::runtime_error("failed to open status file:" + path.string());
}
istrm >> j;
}
linyaps_box::container_status_t ret{};
ret.oci_version = j["ociVersion"];
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) {
ret.status = linyaps_box::container_status_t::runtime_status::STOPPED;
}
ret.bundle = std::string(j["bundle"]);
ret.created = j["created"];
ret.owner = j["owner"];
ret.annotations = j["annotations"];
return ret;
}
} // namespace
void linyaps_box::impl::status_directory::write(const container_status_t &status) const
{
nlohmann::json j = nlohmann::json::object({ { "id", status.ID },
{ "pid", status.PID },
{ "status", to_string(status.status) },
{ "bundle", status.bundle },
{ "created", status.created },
{ "owner", status.owner },
{ "annotations", status.annotations },
{ "ociVersion", status.oci_version } });
utils::atomic_write(this->path / (status.ID + ".json"), j.dump());
}
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 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;
}
}
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)) {
try {
if (entry.is_regular_file() && entry.path().extension() != ".json") {
throw std::runtime_error("invalid extension");
}
auto status = read_status(entry);
ret.push_back(status.ID);
} catch (const std::exception &e) {
LINYAPS_BOX_WARNING() << "Skip " << entry.path() << ": " << e.what();
continue;
}
}
return ret;
}
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;
}
throw std::runtime_error("failed to create status directory");
}

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/status_directory.h"
#include <filesystem>
#include <vector>
namespace linyaps_box::impl {
class status_directory : public virtual linyaps_box::status_directory
{
public:
void write(const container_status_t &status) const override;
[[nodiscard]] auto read(const std::string &id) const -> container_status_t override;
void remove(const std::string &id) const override;
[[nodiscard]] auto list() const -> std::vector<std::string> override;
explicit status_directory(std::filesystem::path dir);
private:
std::filesystem::path path;
};
static_assert(!std::is_abstract_v<status_directory>);
} // namespace linyaps_box::impl

View File

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/impl/table_printer.h"
#include <iostream>
namespace {
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:
return "creating";
case linyaps_box::container_status_t::runtime_status::CREATED:
return "created";
case linyaps_box::container_status_t::runtime_status::RUNNING:
return "running";
case linyaps_box::container_status_t::runtime_status::STOPPED:
return "stopped";
default:
throw std::logic_error("unknown status");
}
}
} // namespace
void linyaps_box::impl::table_printer::print_statuses(const std::vector<container_status_t> &status)
{
int max_length = 4;
for (const auto &s : status) {
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"
<< std::setw(9) << "STATUS" << std::setw(40) << "BUNDLE PATH" << std::setw(31)
<< "CREATED" << std::setw(0) << "OWNER" << '\n';
for (const auto &s : status) {
std::cout << std::left << std::setw(max_length) << s.ID << std::setw(10) << s.PID
<< std::setw(9) << get_status_string(s.status) << std::setw(40) << s.bundle
<< std::setw(31) << s.created << std::setw(0) << s.owner << '\n';
}
std::cout.flush();
}
void linyaps_box::impl::table_printer::print_status(const container_status_t &status)
{
std::cout << "ociVersion\t" << status.oci_version << '\n';
std::cout << "ID\t" << status.ID << '\n';
std::cout << "PID\t" << status.PID << '\n';
std::cout << "status\t" << get_status_string(status.status) << '\n';
std::cout << "bundle\t" << status.bundle << '\n';
std::cout << "created\t" << status.created << '\n';
std::cout << "owner\t" << status.owner << '\n';
std::cout << "annotations" << '\n';
for (const auto &a : status.annotations) {
std::cout << "\t" << a.first << "\t" << a.second << '\n';
}
std::cout.flush();
}

View File

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/printer.h"
namespace linyaps_box::impl {
class table_printer final : public virtual linyaps_box::printer
{
public:
void print_status(const container_status_t &status) final;
void print_statuses(const std::vector<container_status_t> &status) final;
};
static_assert(!std::is_abstract_v<table_printer>);
} // namespace linyaps_box::impl

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// 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

View File

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
namespace linyaps_box {
// prevent object slicing
class interface
{
public:
interface() = default;
virtual ~interface();
interface(const interface &) = delete;
auto operator=(const interface &) -> interface & = delete;
interface(interface &&) = delete;
auto operator=(interface &&) -> interface & = delete;
};
} // namespace linyaps_box

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/printer.h"
namespace linyaps_box {
printer::~printer() = default;
} // namespace linyaps_box

29
src/linyaps_box/printer.h Normal file
View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/container_status.h"
#include "linyaps_box/interface.h"
#include <vector>
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;
};
} // namespace linyaps_box

View File

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#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) }
{
if (!this->status_dir_) {
throw std::invalid_argument("status_dir is nullptr");
}
}
auto linyaps_box::runtime_t::containers()
-> std::unordered_map<std::string, linyaps_box::container_ref>
{
auto container_ids = this->status_dir_->list();
std::unordered_map<std::string, container_ref> containers;
for (const auto &container_id : container_ids) {
containers.emplace(std::piecewise_construct,
std::forward_as_tuple(container_id),
std::forward_as_tuple(*this->status_dir_, container_id));
}
return containers;
}
auto linyaps_box::runtime_t::create_container(const create_container_options_t &options)
-> linyaps_box::container
{
return { *this->status_dir_, options };
}

28
src/linyaps_box/runtime.h Normal file
View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/container.h"
#include "linyaps_box/container_ref.h"
#include "linyaps_box/status_directory.h"
#include <memory>
#include <unordered_map>
namespace linyaps_box {
class runtime_t
{
public:
explicit runtime_t(std::unique_ptr<status_directory> &&status_dir);
auto containers() -> std::unordered_map<std::string, container_ref>;
auto create_container(const create_container_options_t &options) -> container;
private:
std::unique_ptr<status_directory> status_dir_;
};
} // namespace linyaps_box

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// 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

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "linyaps_box/container_status.h"
#include "linyaps_box/interface.h"
#include <vector>
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 auto read(const std::string &id) const -> container_status_t = 0;
virtual void remove(const std::string &id) const = 0;
[[nodiscard]] virtual auto list() const -> std::vector<std::string> = 0;
};
} // namespace linyaps_box

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/utils/atomic_write.h"
#include <fstream>
void linyaps_box::utils::atomic_write(const std::filesystem::path &path, const std::string &content)
{
std::filesystem::path temp_path = path;
temp_path += ".tmp";
std::ofstream temp_file(temp_path);
if (!temp_file.is_open()) {
throw std::runtime_error("failed to open temporary file");
}
temp_file << content;
temp_file.close();
std::filesystem::rename(temp_path, path);
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <filesystem>
namespace linyaps_box::utils {
void atomic_write(const std::filesystem::path &path, const std::string &content);
} // namespace linyaps_box::utils

View File

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <linux/magic.h>
#include <linyaps_box/utils/cgroups.h>
#include <sys/statfs.h>
#include <filesystem>
#include <system_error>
constexpr auto cgroup_root = "/sys/fs/cgroups";
auto linyaps_box::utils::get_cgroup_type() -> linyaps_box::utils::cgroup_t
{
static auto cgroup_type = []() -> cgroup_t {
struct statfs stat{};
auto ret = statfs(cgroup_root, &stat);
if (ret < 0) {
throw std::system_error(errno,
std::system_category(),
std::string{ "statfs " } + cgroup_root);
}
// for cgroup v2, filesystem type is cgroups2
if (stat.f_type == CGROUP2_SUPER_MAGIC) {
return cgroup_t::unified;
}
// for cgroup v1, filesystem type is tmpfs
if (stat.f_type != TMPFS_MAGIC) {
throw std::system_error(-1,
std::system_category(),
std::string{ "invalid file system type on " } + cgroup_root);
}
// https://man7.org/linux/man-pages/man7/cgroups.7.html
auto unified = std::filesystem::path{ cgroup_root } / "unified";
ret = statfs(unified.c_str(), &stat);
if (ret < 0) {
if (errno != ENOENT) {
throw std::system_error(errno,
std::system_category(),
"statfs " + unified.string());
}
return cgroup_t::legacy;
}
return stat.f_type == CGROUP2_SUPER_MAGIC ? cgroup_t::hybrid : cgroup_t::legacy;
}();
return cgroup_type;
}

View File

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <cstdint>
namespace linyaps_box::utils {
enum class cgroup_t : std::uint8_t { unified, legacy, hybrid };
auto get_cgroup_type() -> cgroup_t;
} // namespace linyaps_box::utils

View File

@ -0,0 +1,119 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "linyaps_box/utils/close_range.h"
#include "linyaps_box/utils/defer.h"
#include "linyaps_box/utils/log.h"
#include <cstring>
#include <system_error>
#include <dirent.h>
#include <fcntl.h>
namespace {
void syscall_close_range(uint fd, uint max_fd, int flags)
{
auto ret = syscall(__NR_close_range, fd, max_fd, flags);
if (ret < 0) {
throw std::system_error(errno, std::generic_category(), "close_range");
}
}
void close_range_fallback(uint first, uint last, int flags)
{
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
throw std::runtime_error("the fallback implementation of close_range dose not support flag "
"'CLOSE_RANGE_UNSHARE'");
}
auto *dir = opendir("/proc/self/fd");
if (dir == nullptr) {
throw std::system_error(errno, std::generic_category(), "opendir /proc/self/fd");
}
auto close_dir = make_defer([dir]() noexcept {
if (closedir(dir) < 0) {
LINYAPS_BOX_WARNING() << "closedir /proc/self/fd failed: " << strerror(errno)
<< ", but this may not be a problem";
}
});
// except self fd
// use opendir instead of std::filesystem::directory_iterator
// because we should skip the file descriptor which is opened by opendir
auto self_fd = dirfd(dir);
if (self_fd < 0) {
throw std::system_error(errno, std::generic_category(), "dirfd");
}
struct dirent *next{ nullptr };
while ((next = ::readdir(dir)) != nullptr) {
const std::string name{ next->d_name[0] };
if (name == "." || name == "..") { // skip "." and ".."
continue;
}
auto fd = std::stoi(name);
if (fd == self_fd) {
continue;
}
if (static_cast<uint>(fd) < first || static_cast<uint>(fd) > last) {
continue;
}
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(),
"failed to set up close-on-exec to " + name);
}
} else {
if (::close(fd) < 0) {
throw std::system_error(errno, std::generic_category(), "failed to close " + name);
}
}
}
}
} // namespace
void linyaps_box::utils::close_range(uint first, uint last, int flags)
{
LINYAPS_BOX_DEBUG() << "close_range (" << first << ", " << last << ")" << "with flags "
<< [flags]() -> std::string {
std::stringstream ss;
ss << '[';
if ((static_cast<uint>(flags) & CLOSE_RANGE_CLOEXEC) != 0) {
ss << "CLOSE_RANGE_CLOEXEC ";
}
if ((static_cast<uint>(flags) & CLOSE_RANGE_UNSHARE) != 0) {
ss << "CLOSE_RANGE_UNSHARE ";
}
ss << ']';
return ss.str();
}();
static bool support_close_range{ true };
if (!support_close_range) {
close_range_fallback(first, last, flags);
return;
}
try {
syscall_close_range(first, last, flags);
} catch (const std::system_error &e) {
auto code = e.code().value();
if (code != ENOSYS) {
throw;
}
support_close_range = false;
close_range_fallback(first, last, flags);
}
}

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
// NOTE:
// close_range is support since Linux 5.9, glibc 2.34
// but we need to support older kernels and glibc
// so we need to implement it ourselves
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
// from linux/close_range.h
#ifndef CLOSE_RANGE_UNSHARE
#define CLOSE_RANGE_UNSHARE (1U << 1)
#endif
#ifndef CLOSE_RANGE_CLOEXEC
#define CLOSE_RANGE_CLOEXEC (1U << 2) // after linux 5.11
#endif
// use __NR_* instead of SYS_*
// https://man7.org/linux/man-pages/man2/syscalls.2.html
#ifndef __NR_close_range
#define __NR_close_range 436
#endif
namespace linyaps_box::utils {
void close_range(uint first, uint last, int flags);
} // namespace linyaps_box::utils

View File

@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <cstdint>
#include <exception>
#include <type_traits>
#include <utility>
// Type constraints for defer, the cleanup function should not throw exceptions
template<typename Fn>
constexpr bool compatible_defer = std::is_nothrow_invocable_r_v<void, Fn>;
// Execution policies for defer
enum class defer_policy : std::uint8_t {
always, // Always execute the cleanup function
on_error // Execute the cleanup function only when an exception is active
};
// defer executes the function based on the specified policy when the object is destroyed
template<typename Fn,
defer_policy Policy = defer_policy::always,
std::enable_if_t<compatible_defer<Fn>, bool> = true>
struct defer
{
explicit defer(Fn &&fn) noexcept
: fn_(std::move(fn))
{
}
defer(const defer &) = delete;
auto operator=(const defer &) -> defer & = delete;
defer(defer &&other) noexcept
: fn_(std::move(other.fn_))
, cancelled(other.cancelled)
{
other.cancelled = true;
}
defer &operator=(defer &&other) noexcept
{
if (this == &other) {
return *this;
}
if (!cancelled) {
execute();
}
fn_ = std::move(other.fn_);
cancelled = other.cancelled;
other.cancelled = true;
return *this;
}
~defer() noexcept
{
if (!cancelled) {
execute();
}
}
void cancel() noexcept { cancelled = true; }
[[nodiscard]] auto is_cancelled() const noexcept -> bool { return cancelled; }
private:
void execute() noexcept
{
if constexpr (Policy == defer_policy::always) {
fn_();
} else if constexpr (Policy == defer_policy::on_error) {
if (std::uncaught_exceptions() > 0) {
fn_();
}
}
}
Fn fn_;
bool cancelled{ false };
};
// Helper functions to create defer objects
template<typename Fn>
auto make_defer(Fn &&fn) noexcept
{
return defer<std::decay_t<Fn>>(std::forward<Fn>(fn));
}
// This project use exception to indicate the failure of a function, so we provide a way to create
// a defer object that executes the cleanup function only when an exception is active during
// destruction
template<typename Fn>
auto make_errdefer(Fn &&fn) noexcept
{
return defer<std::decay_t<Fn>, defer_policy::on_error>(std::forward<Fn>(fn));
}

Some files were not shown because too many files have changed in this diff Show More