├── .clang-format ├── .github └── workflows │ ├── build.yml │ ├── codeql.yml │ └── test.yml ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ChangeLog ├── LICENSE ├── MAINTAINERS ├── Makefile ├── README ├── README.md ├── SECURITY.md ├── TERMS_OF_USE.md ├── autogen.sh ├── build.c ├── build └── build.sh ├── config.mk.in ├── configure.ac ├── doc ├── Build.md ├── FAQ.md ├── Integration.md ├── Security.md ├── USAGE.md ├── askllm.md ├── init.md ├── mount.md └── rurienv.md ├── logo ├── logo.png ├── logo.py ├── requirements.txt └── rurifetch.png ├── src ├── README.md ├── caplist.c ├── cgroup.c ├── chroot.c ├── config.c ├── cprintf.c ├── easteregg │ ├── README.md │ ├── action.c │ ├── include │ │ └── nekofeng.h │ ├── layer.c │ ├── nekofeng.c │ └── typewriter.c ├── elf-magic.c ├── include │ ├── cprintf.h │ ├── elf-magic.h │ ├── hostarch.h │ ├── k2v.h │ ├── ruri.h │ └── version.h ├── info.c ├── k2v.c ├── main.c ├── mount.c ├── passwd.c ├── ps.c ├── rootless.c ├── ruri.c ├── rurienv.c ├── rurifetch.c ├── seccomp.c ├── signal.c ├── umount.c └── unshare.c └── test ├── Makefile ├── README.md ├── check_flag.c ├── clean.sh ├── global.sh ├── init-root-test.sh ├── root ├── 1-basic.sh ├── 2-container_runtime.sh ├── 3-capability.sh ├── 4-mount.sh ├── 5-ro_mount.sh ├── 6-env.sh ├── 7-ps.sh ├── 8-secure-option.sh └── 9-cross_arch.sh ├── test-root.c └── test-root.sh /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 11. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | ############## 12 | # This file is from kernel.org, it is only used to format the code. 13 | # It's not part of my project. 14 | # The original file can be found at: 15 | # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/.clang-format 16 | --- 17 | AccessModifierOffset: -4 18 | AlignAfterOpenBracket: Align 19 | AlignConsecutiveAssignments: false 20 | AlignConsecutiveDeclarations: false 21 | AlignEscapedNewlines: Left 22 | AlignOperands: true 23 | AlignTrailingComments: false 24 | AllowAllParametersOfDeclarationOnNextLine: false 25 | AllowShortBlocksOnASingleLine: false 26 | AllowShortCaseLabelsOnASingleLine: false 27 | AllowShortFunctionsOnASingleLine: None 28 | AllowShortIfStatementsOnASingleLine: false 29 | AllowShortLoopsOnASingleLine: false 30 | AlwaysBreakAfterDefinitionReturnType: None 31 | AlwaysBreakAfterReturnType: None 32 | AlwaysBreakBeforeMultilineStrings: false 33 | AlwaysBreakTemplateDeclarations: false 34 | BinPackArguments: true 35 | BinPackParameters: true 36 | BraceWrapping: 37 | AfterClass: false 38 | AfterControlStatement: false 39 | AfterEnum: false 40 | AfterFunction: true 41 | AfterNamespace: true 42 | AfterObjCDeclaration: false 43 | AfterStruct: false 44 | AfterUnion: false 45 | AfterExternBlock: false 46 | BeforeCatch: false 47 | BeforeElse: false 48 | IndentBraces: false 49 | SplitEmptyFunction: true 50 | SplitEmptyRecord: true 51 | SplitEmptyNamespace: true 52 | BreakBeforeBinaryOperators: None 53 | BreakBeforeBraces: Custom 54 | BreakBeforeInheritanceComma: false 55 | BreakBeforeTernaryOperators: false 56 | BreakConstructorInitializersBeforeComma: false 57 | BreakConstructorInitializers: BeforeComma 58 | BreakAfterJavaFieldAnnotations: false 59 | BreakStringLiterals: false 60 | ColumnLimit: 114514 61 | CommentPragmas: '^ IWYU pragma:' 62 | CompactNamespaces: false 63 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 64 | ConstructorInitializerIndentWidth: 8 65 | ContinuationIndentWidth: 8 66 | Cpp11BracedListStyle: false 67 | DerivePointerAlignment: false 68 | DisableFormat: false 69 | ExperimentalAutoDetectBinPacking: false 70 | FixNamespaceComments: false 71 | 72 | 73 | ForEachMacros: 74 | IncludeBlocks: Preserve 75 | IncludeCategories: 76 | - Regex: '.*' 77 | Priority: 1 78 | IncludeIsMainRegex: '(Test)?$' 79 | IndentCaseLabels: false 80 | IndentGotoLabels: false 81 | IndentPPDirectives: None 82 | IndentWidth: 8 83 | IndentWrappedFunctionNames: false 84 | JavaScriptQuotes: Leave 85 | JavaScriptWrapImports: true 86 | KeepEmptyLinesAtTheStartOfBlocks: false 87 | MacroBlockBegin: '' 88 | MacroBlockEnd: '' 89 | MaxEmptyLinesToKeep: 1 90 | NamespaceIndentation: None 91 | ObjCBinPackProtocolList: Auto 92 | ObjCBlockIndentWidth: 8 93 | ObjCSpaceAfterProperty: true 94 | ObjCSpaceBeforeProtocolList: true 95 | 96 | # Taken from git's rules 97 | PenaltyBreakAssignment: 10 98 | PenaltyBreakBeforeFirstCallParameter: 30 99 | PenaltyBreakComment: 10 100 | PenaltyBreakFirstLessLess: 0 101 | PenaltyBreakString: 10 102 | PenaltyExcessCharacter: 100 103 | PenaltyReturnTypeOnItsOwnLine: 60 104 | 105 | PointerAlignment: Right 106 | ReflowComments: true 107 | SortIncludes: false 108 | SortUsingDeclarations: false 109 | SpaceAfterCStyleCast: false 110 | SpaceAfterTemplateKeyword: true 111 | SpaceBeforeAssignmentOperators: true 112 | SpaceBeforeCtorInitializerColon: true 113 | SpaceBeforeInheritanceColon: true 114 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 115 | SpaceBeforeRangeBasedForLoopColon: true 116 | SpaceInEmptyParentheses: false 117 | SpacesBeforeTrailingComments: 1 118 | SpacesInAngles: false 119 | SpacesInContainerLiterals: false 120 | SpacesInCStyleCastParentheses: false 121 | SpacesInParentheses: false 122 | SpacesInSquareBrackets: false 123 | Standard: Cpp03 124 | TabWidth: 8 125 | UseTab: Always -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "main" ] 7 | paths: 8 | - 'src/**' 9 | - 'build/**' 10 | - 'build.c' 11 | - '.github/**' 12 | tags: 13 | - "v*" 14 | pull_request: 15 | 16 | jobs: 17 | update: 18 | name: Fetch Latest Version 19 | runs-on: ubuntu-latest 20 | outputs: 21 | release: ${{ steps.fetch_version.outputs.release }} 22 | version: ${{ steps.fetch_version.outputs.version }} 23 | release_name: ${{ steps.fetch_version.outputs.release_name }} 24 | build_time: ${{ steps.fetch_version.outputs.build_time }} 25 | steps: 26 | - name: fetch latest version 27 | id: fetch_version 28 | run: | 29 | if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then 30 | version="${{ github.ref_name }}" 31 | release_name="ruri ${{ github.ref_name }} Release" 32 | else 33 | response=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -L "https://api.github.com/repos/${{ github.repository }}/releases/latest") 34 | version=$(echo "$response" | jq -r .tag_name) 35 | release_name=$(echo "$response" | jq -r .name) 36 | fi 37 | 38 | if [[ -n "$version" && "$version" != "null" && -n "$release_name" && "$release_name" != "null" ]]; then 39 | echo "release=true" >> $GITHUB_OUTPUT 40 | else 41 | echo "release=false" >> $GITHUB_OUTPUT 42 | fi 43 | 44 | build_time="UTC $(TZ=UTC date '+%Y%m%d%H%M')" 45 | echo "version=$version" >> $GITHUB_OUTPUT 46 | echo "release_name=$release_name" >> $GITHUB_OUTPUT 47 | echo "build_time=$build_time" >> $GITHUB_OUTPUT 48 | 49 | build-arch: 50 | runs-on: ubuntu-latest 51 | permissions: 52 | contents: read 53 | strategy: 54 | matrix: 55 | arch: [x86_64, x86, aarch64, armhf, armv7, ppc64le, loongarch64, riscv64, s390x] 56 | #arch: [x86_64, x86, aarch64, armhf, armv7, ppc64le, loongarch64, riscv64] 57 | env: 58 | ARCHITECTURE: ${{ matrix.arch }} 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@v4 62 | 63 | - name: Install Dependencies 64 | run: | 65 | sudo apt-get update 66 | sudo apt-get --no-install-recommends -y install \ 67 | binutils build-essential libcap-dev libseccomp-dev make qemu-user-static xz-utils 68 | 69 | - name: Build ruri for containers 70 | run: | 71 | cd build 72 | git clone --depth 1 https://github.com/moe-hacker/ruri.git 73 | cd ruri 74 | cc -Wl,--gc-sections -static src/*.c src/easteregg/*.c -o ruri -lcap -lseccomp -lpthread 75 | cp ./ruri ../../ 76 | 77 | - name: Download alpine rootfs for ${{ matrix.arch }} 78 | env: 79 | URL: https://dl-cdn.alpinelinux.org/alpine/v3.22/releases 80 | run: | 81 | cd build 82 | mkdir -p $ARCHITECTURE 83 | 84 | wget -q https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O yq 85 | sudo mv yq /usr/local/bin/yq 86 | sudo chmod +x /usr/local/bin/yq 87 | 88 | FILE=$(curl -s "$URL/$ARCHITECTURE/latest-releases.yaml" | yq '.[] | select(.flavor == "alpine-minirootfs") | .file') 89 | wget "$URL/$ARCHITECTURE/$FILE" 90 | tar -xzf "$FILE" -C "$ARCHITECTURE" 91 | 92 | - name: Build for ${{ matrix.arch }} 93 | shell: bash 94 | run: | 95 | cd build 96 | cp build.sh "$ARCHITECTURE/build.sh" 97 | sudo chmod +x ../ruri 98 | sudo chmod +x "$ARCHITECTURE/build.sh" 99 | 100 | case "$ARCHITECTURE" in 101 | x86_64|x86) 102 | sudo ../ruri ./$ARCHITECTURE /bin/sh /build.sh 103 | ;; 104 | aarch64|armhf|ppc64le|armv7|riscv64|s390x|loongarch64) 105 | if [ "$ARCHITECTURE" = "armv7" ]; then 106 | sudo ../ruri -a armv7 -q /usr/bin/qemu-arm-static ./$ARCHITECTURE /bin/sh /build.sh 107 | else 108 | sudo ../ruri -a $ARCHITECTURE -q /usr/bin/qemu-$ARCHITECTURE-static ./$ARCHITECTURE /bin/sh /build.sh 109 | fi 110 | ;; 111 | esac 112 | 113 | (cd $ARCHITECTURE/output && tar -cf ../../../$ARCHITECTURE.tar .) 114 | (cd $ARCHITECTURE/output3 && tar -cf ../../../$ARCHITECTURE-core.tar .) 115 | if [[ "$ARCHITECTURE" =~ ^(x86_64|x86|aarch64|armhf|ppc64le|armv7)$ ]]; then 116 | (cd $ARCHITECTURE/output2 && tar -cf ../../../$ARCHITECTURE-upx.tar .) 117 | fi 118 | 119 | cd $GITHUB_WORKSPACE 120 | if [[ "$ARCHITECTURE" == "x86" ]]; then 121 | mv $ARCHITECTURE.tar i386.tar 122 | mv $ARCHITECTURE-upx.tar i386-upx.tar 123 | mv $ARCHITECTURE-core.tar i386-core.tar 124 | fi 125 | 126 | - name: Upload artifacts 127 | if: true 128 | uses: actions/upload-artifact@v4 129 | with: 130 | name: ruri-${{ matrix.arch }} 131 | path: | 132 | ./*.tar 133 | retention-days: 7 134 | 135 | release: 136 | name: Push Release 137 | needs: [update, build-arch] 138 | runs-on: ubuntu-latest 139 | permissions: 140 | contents: write 141 | if: | 142 | (github.event_name == 'push' && needs.update.outputs.release == 'true') || 143 | startsWith(github.ref, 'refs/tags/') 144 | steps: 145 | - name: Download All Build 146 | uses: actions/download-artifact@v4 147 | with: 148 | path: ./all 149 | 150 | - name: Move all .tar files 151 | run: | 152 | find ./all -type f -name "*.tar" -exec mv {} ./ \; 153 | # Upload only when file > 50kb 154 | for file in ./*.tar; do 155 | if [ ! -s "$file" ] || [ $(stat -c%s "$file") -lt 51200 ]; then 156 | echo "Removing $file as it is smaller than 50kb" 157 | rm "$file" 158 | fi 159 | done 160 | 161 | - name: Release 162 | uses: softprops/action-gh-release@v2.2.2 163 | env: 164 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 165 | with: 166 | tag_name: ${{ needs.update.outputs.version }} 167 | name: ${{ needs.update.outputs.release_name }} 168 | body: | 169 | This is ruri binary release. 170 | NOTE: 171 | *-upx means the binary is upx compressed. 172 | *-core means the binary is built without libseccomp and libcap, and with .rurienv disabled. 173 | ruri use musl as libc to build by default (in alpine container), for smaller binary size and better security. 174 | Build time: ${{ needs.update.outputs.build_time }} 175 | files: | 176 | *.tar 177 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Advanced" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '16 23 * * 1' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: c-cpp 47 | build-mode: manual 48 | - language: python 49 | build-mode: none 50 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 51 | # Use `c-cpp` to analyze code written in C, C++ or both 52 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 53 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 54 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 55 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 56 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 57 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 58 | steps: 59 | - name: Checkout repository 60 | uses: actions/checkout@v4 61 | 62 | # Initializes the CodeQL tools for scanning. 63 | - name: Initialize CodeQL 64 | uses: github/codeql-action/init@v3 65 | with: 66 | languages: ${{ matrix.language }} 67 | build-mode: ${{ matrix.build-mode }} 68 | # If you wish to specify custom queries, you can do so here or in a config file. 69 | # By default, queries listed here will override any specified in a config file. 70 | # Prefix the list here with "+" to use these queries and those in the config file. 71 | 72 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 73 | # queries: security-extended,security-and-quality 74 | 75 | # If the analyze step fails for one of the languages you are analyzing with 76 | # "We were unable to automatically build your code", modify the matrix above 77 | # to set the build mode to "manual" for that language. Then modify this step 78 | # to build your code. 79 | # ℹ️ Command-line programs to run using the OS shell. 80 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 81 | - if: matrix.build-mode == 'manual' 82 | shell: bash 83 | run: | 84 | sudo apt update 85 | sudo apt install -y \ 86 | make \ 87 | clang \ 88 | libseccomp-dev \ 89 | libcap-dev \ 90 | libc6-dev \ 91 | binutils 92 | aclocal 93 | autoconf 94 | ./configure --enable-debug --enable-dev 95 | make 96 | 97 | - name: Perform CodeQL Analysis 98 | uses: github/codeql-action/analyze@v3 99 | with: 100 | category: "/language:${{matrix.language}}" 101 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run autotest 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "main" ] 7 | paths: 8 | - 'src/**' 9 | - 'build/**' 10 | - 'test/**' 11 | - 'configure.ac' 12 | - 'Makefile' 13 | pull_request: 14 | 15 | jobs: 16 | test: 17 | name: Test 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Test 24 | run: | 25 | sudo apt update && sudo apt install -y git wget 26 | sudo apt install --no-install-recommends -y curl xz-utils \ 27 | make \ 28 | clang \ 29 | libseccomp-dev \ 30 | libcap-dev \ 31 | libc6-dev \ 32 | binutils qemu-user-static uidmap 33 | make -C test 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary 2 | ruri 3 | ruri-amd64.zip 4 | ruri-arm64.zip 5 | # Editor configuration. 6 | .vscode/ 7 | # Gdb log 8 | peda-* 9 | # Build dir. 10 | out 11 | # Tmp file. 12 | TODO 13 | *.save 14 | a.out 15 | t.c 16 | config.mk 17 | *.pyc 18 | debian/ruri 19 | debian/source 20 | debian/.debhelper 21 | debian/debhelper-build-stamp 22 | debian/files 23 | tmpdir* 24 | test-root 25 | cmake_install.cmake 26 | CMakeCache.txt 27 | CMakeFiles 28 | build/.cmake 29 | build/compile_commands.json 30 | build/Makefile 31 | build/build.ninja 32 | build/.ninja_deps 33 | build/.ninja_log 34 | autom4te.cache 35 | aclocal.m4 36 | config.log 37 | config.status 38 | config.h 39 | config.h.in 40 | compile 41 | configure~ 42 | install-sh 43 | missing 44 | configure 45 | Makefile.in 46 | 47 | # VitePress 48 | docs/.vitepress/cache 49 | docs/.vitepress/.temp 50 | docs/.vitepress/dist 51 | node_modules 52 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(ruri LANGUAGES C) 3 | 4 | # ============================== 5 | # Sources & Executable 6 | # ============================== 7 | file(GLOB SOURCES CONFIGURE_DEPENDS 8 | ${CMAKE_SOURCE_DIR}/src/*.c 9 | ${CMAKE_SOURCE_DIR}/src/easteregg/*.c 10 | ) 11 | 12 | add_executable(ruri ${SOURCES}) 13 | 14 | # ============================== 15 | # Options 16 | # ============================== 17 | option(ENABLE_DEBUG "Enable debug build" OFF) 18 | option(ENABLE_STATIC "Enable static linking" OFF) 19 | option(DISABLE_LIBCAP "Disable cap library linking" OFF) 20 | option(DISABLE_LIBSECCOMP "Disable seccomp library linking" OFF) 21 | option(DISABLE_RURIENV "Disable env in ruri" OFF) 22 | option(STRIP_DEBUGINFO "Strip debuginfo from ruri binary" ON) 23 | 24 | # ============================== 25 | # Platform Check 26 | # ============================== 27 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") 28 | message(FATAL_ERROR "ruri is only supported on Linux platforms.") 29 | endif() 30 | 31 | # ============================== 32 | # Build Metadata 33 | # ============================== 34 | execute_process(COMMAND date "+%Y-%m-%d" OUTPUT_VARIABLE CMAKE_CURRENT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) 35 | execute_process(COMMAND date "+%H:%M:%S" OUTPUT_VARIABLE CMAKE_CURRENT_TIME OUTPUT_STRIP_TRAILING_WHITESPACE) 36 | 37 | execute_process( 38 | COMMAND git rev-parse --short HEAD 39 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 40 | OUTPUT_VARIABLE GIT_COMMIT_ID 41 | OUTPUT_STRIP_TRAILING_WHITESPACE 42 | ) 43 | 44 | # ============================== 45 | # Compiler & Toolchain Setup 46 | # ============================== 47 | # ccache 48 | find_program(CCACHE_FOUND ccache) 49 | if(CCACHE_FOUND) 50 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) 51 | set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) 52 | message(STATUS "Using ccache: ${CCACHE_FOUND}") 53 | else() 54 | message(STATUS "Ccache not found. Compiling without cache.") 55 | endif() 56 | 57 | # TinyCC special handling 58 | if(CMAKE_C_COMPILER MATCHES "tcc") 59 | set(_arch ${CMAKE_SYSTEM_PROCESSOR}) 60 | if(_arch MATCHES "x86_64") 61 | set(CMAKE_LIBRARY_PATH "/usr/lib/x86_64-linux-gnu/") 62 | set(CMAKE_INCLUDE_PATH "/usr/include/x86_64-linux-gnu/") 63 | elseif(_arch MATCHES "aarch64") 64 | set(CMAKE_LIBRARY_PATH "/usr/lib/aarch64-linux-gnu/") 65 | set(CMAKE_INCLUDE_PATH "/usr/include/aarch64-linux-gnu/") 66 | elseif(_arch MATCHES "armv7l") 67 | set(CMAKE_LIBRARY_PATH "/usr/lib/arm-linux-gnueabihf/") 68 | set(CMAKE_INCLUDE_PATH "/usr/include/arm-linux-gnueabihf/") 69 | elseif(_arch MATCHES "i386") 70 | set(CMAKE_LIBRARY_PATH "/usr/lib/i386-linux-gnu/") 71 | set(CMAKE_INCLUDE_PATH "/usr/include/i386-linux-gnu/") 72 | elseif(_arch MATCHES "powerpc64le") 73 | set(CMAKE_LIBRARY_PATH "/usr/lib/powerpc64le-linux-gnu/") 74 | set(CMAKE_INCLUDE_PATH "/usr/include/powerpc64le-linux-gnu/") 75 | endif() 76 | add_definitions(-D__VERSION__="TinyCC") 77 | add_definitions(-D__TIMESTAMP__="${CMAKE_CURRENT_DATE} ${CMAKE_CURRENT_TIME}") 78 | endif() 79 | 80 | # ============================== 81 | # Required Headers 82 | # ============================== 83 | include(CheckIncludeFile) 84 | 85 | set(REQUIRED_HEADERS 86 | time.h 87 | grp.h 88 | fcntl.h 89 | "sys/ioctl.h" 90 | "sys/mount.h" 91 | "sys/socket.h" 92 | "linux/fs.h" 93 | "linux/version.h" 94 | "linux/sched.h" 95 | "sys/capability.h" 96 | seccomp.h 97 | pthread.h 98 | ) 99 | 100 | foreach(hdr IN LISTS REQUIRED_HEADERS) 101 | string(REPLACE "/" "_" CHECK_NAME "HAVE_${hdr}") 102 | string(REPLACE "." "_" CHECK_NAME "${CHECK_NAME}") 103 | check_include_file("${hdr}" ${CHECK_NAME}) 104 | if(NOT ${CHECK_NAME}) 105 | message(FATAL_ERROR "Missing required header: ${hdr}") 106 | endif() 107 | endforeach() 108 | 109 | # ============================== 110 | # Compiler Flags 111 | # ============================== 112 | set(CANDIDATE_CFLAGS 113 | "-flto=auto" 114 | "-pie" 115 | "-fstack-protector-all" 116 | "-fdata-sections" 117 | "-fno-omit-frame-pointer" 118 | "-fno-stack-protector" 119 | "-ftrivial-auto-var-init=pattern" 120 | "-fstack-clash-protection" 121 | "-Wno-unused-result" 122 | "-mshstk" 123 | "-ffunction-sections" 124 | "-Wall" 125 | "-Wextra" 126 | "-Wconversion" 127 | "-pedantic" 128 | "-pipe" 129 | ) 130 | 131 | # Test and apply supported flags 132 | foreach(flag IN LISTS CANDIDATE_CFLAGS) 133 | try_compile(HAS_FLAG ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/test/check_flag.c COMPILE_DEFINITIONS ${flag}) 134 | if(HAS_FLAG) 135 | target_compile_options(ruri PRIVATE ${flag}) 136 | message(STATUS "Compiler supports flag: ${flag}") 137 | else() 138 | message(WARNING "Compiler does not support flag: ${flag}") 139 | endif() 140 | endforeach() 141 | 142 | # LDFLAGS (applied via target_link_options later) 143 | set(RURI_LDFLAGS 144 | "-Wl,-z,relro" 145 | "-Wl,-z,noexecstack" 146 | "-Wl,-z,now" 147 | "-Wl,--gc-sections" 148 | "-Wl,--disable-new-dtags" 149 | "-Wl,--build-id=sha1" 150 | "-Wl,-z,norelro" 151 | "-Wl,-z,execstack" 152 | ) 153 | 154 | # ============================== 155 | # Build Type & Definitions 156 | # ============================== 157 | if(ENABLE_DEBUG) 158 | message(WARNING "Warning: DEBUG mode is enabled") 159 | target_compile_definitions(ruri PRIVATE 160 | DEBUG_BUILD 161 | RURI_DEBUG 162 | RURI_DEV 163 | ) 164 | target_compile_options(ruri PRIVATE -g3 -O0) 165 | else() 166 | target_compile_options(ruri PRIVATE -O2) 167 | target_compile_definitions(ruri PRIVATE NDEBUG) 168 | endif() 169 | 170 | if(DISABLE_RURIENV) 171 | target_compile_definitions(ruri PRIVATE DISABLE_RURIENV) 172 | endif() 173 | 174 | if(DISABLE_RURIENV AND DISABLE_LIBCAP AND DISABLE_LIBSECCOMP) 175 | target_compile_definitions(ruri PRIVATE RURI_CORE_ONLY) 176 | endif() 177 | 178 | target_compile_definitions(ruri PRIVATE 179 | RURI_COMMIT_ID="${GIT_COMMIT_ID}" 180 | _FILE_OFFSET_BITS=64 181 | ) 182 | 183 | # Position-independent executable 184 | set_target_properties(ruri PROPERTIES POSITION_INDEPENDENT_CODE OFF) 185 | target_compile_options(ruri PRIVATE -fPIE) 186 | 187 | # Apply LDFLAGS 188 | target_link_options(ruri PRIVATE ${RURI_LDFLAGS}) 189 | 190 | # Static linking 191 | if(ENABLE_STATIC) 192 | target_link_options(ruri PRIVATE -static) 193 | endif() 194 | 195 | # ============================== 196 | # Library Linking (Linux only) 197 | # ============================== 198 | find_library(LCAP cap) 199 | find_library(LSECCOMP seccomp) 200 | find_library(LPTHREAD pthread) 201 | 202 | if(NOT DISABLE_LIBCAP AND NOT LCAP) 203 | message(FATAL_ERROR "Library 'cap' is required but not found.") 204 | endif() 205 | if(NOT DISABLE_LIBSECCOMP AND NOT LSECCOMP) 206 | message(FATAL_ERROR "Library 'seccomp' is required but not found.") 207 | endif() 208 | 209 | if(DISABLE_LIBCAP AND DISABLE_LIBSECCOMP) 210 | target_compile_definitions(ruri PRIVATE DISABLE_LIBCAP DISABLE_LIBSECCOMP) 211 | elseif(DISABLE_LIBCAP) 212 | target_compile_definitions(ruri PRIVATE DISABLE_LIBCAP) 213 | target_link_libraries(ruri PRIVATE ${LSECCOMP}) 214 | elseif(DISABLE_LIBSECCOMP) 215 | target_compile_definitions(ruri PRIVATE DISABLE_LIBSECCOMP) 216 | target_link_libraries(ruri PRIVATE ${LCAP}) 217 | else() 218 | target_link_libraries(ruri PRIVATE ${LCAP} ${LSECCOMP}) 219 | endif() 220 | 221 | # Always link pthread 222 | target_link_libraries(ruri PRIVATE ${LPTHREAD}) 223 | 224 | # ============================== 225 | # Debug Info Stripping 226 | # ============================== 227 | if(STRIP_DEBUGINFO) 228 | try_compile(HAS_STRIP_ALL ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/test/check_flag.c COMPILE_DEFINITIONS -Wl,--strip-all) 229 | if(HAS_STRIP_ALL) 230 | target_link_options(ruri PRIVATE -Wl,--strip-all) 231 | message(STATUS "Using linker flag: -Wl,--strip-all") 232 | else() 233 | # Fallback: use strip command post-build 234 | find_program(STRIP_TOOL strip) 235 | if(STRIP_TOOL) 236 | add_custom_command(TARGET ruri POST_BUILD 237 | COMMAND ${STRIP_TOOL} ruri 238 | COMMENT "Stripping debug symbols" 239 | ) 240 | else() 241 | message(WARNING "strip not found; debug info may remain.") 242 | endif() 243 | endif() 244 | endif() 245 | 246 | # ============================== 247 | # Install & Dev Tools 248 | # ============================== 249 | install(TARGETS ruri DESTINATION bin) 250 | 251 | add_custom_target( 252 | tidy 253 | COMMAND clang-tidy 254 | --checks=*, 255 | -clang-analyzer-security.insecureAPI.strcpy, 256 | -altera-unroll-loops, 257 | -cert-err33-c, 258 | -concurrency-mt-unsafe, 259 | -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, 260 | -readability-function-cognitive-complexity, 261 | -cppcoreguidelines-avoid-magic-numbers, 262 | -readability-magic-numbers, 263 | -bugprone-easily-swappable-parameters, 264 | -cert-err34-c, 265 | -misc-include-cleaner, 266 | -readability-identifier-length, 267 | -bugprone-signal-handler, 268 | -cert-msc54-cpp, 269 | -cert-sig30-c, 270 | -altera-id-dependent-backward-branch, 271 | -bugprone-suspicious-realloc-usage, 272 | -hicpp-signed-bitwise, 273 | -clang-analyzer-security.insecureAPI.UncheckedReturn 274 | --list-checks 275 | ${SOURCES} 276 | -- -lpthread -lseccomp -lcap -Wall -Wextra 277 | COMMENT "Running clang-tidy" 278 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 279 | ) 280 | 281 | add_custom_target( 282 | format 283 | COMMAND clang-format -i ${SOURCES} ${CMAKE_SOURCE_DIR}/include/*.h 284 | COMMENT "Formatting source code with clang-format" 285 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 286 | ) 287 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | TODO: I'll add it when we have more active contributors :( 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Commit Messages 2 | We recommend, but do not strictly require, using a prefix for commit messages to indicate the type of change. 3 | The currently planned prefixes are: 4 | - feat: Add a new feature. 5 | - fix: Fix a bug. 6 | - sec: Security hardening. 7 | - build: Build system changes. 8 | - ci: Continuous integration changes. 9 | - doc: Documentation updates. 10 | - non: Non-core features (e.g., easter eggs, minor tweaks, non-critical changes). 11 | 12 | # New Features 13 | All new features must maintain backward compatibility. 14 | A new feature should have reasonably broad applicability rather than serving only a highly specific use case. 15 | When implementing a feature, avoid introducing new dependencies to the final binary whenever possible. 16 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | # v3.9.3: 2 | * Add `-g` option: `--skip-setgroups`. 3 | * Make setgroups() enabled for root user by default. 4 | # v3.9.2: 5 | * Add `-z` option: `--enable-tty-signals`. 6 | * Fix devpts mount. 7 | # v3.9.1: 8 | * Support prefix for errno action in `--deny-syscall` option. 9 | * Add `-Q` option: `--mask-path`. 10 | * Load ruri from memfd to avoid leak of ruri binary. 11 | # v3.9.0: 12 | * Support part of bsd-style command-line usage. 13 | * Fix built-in seccomp profile. 14 | * Add `-X` option: `--deny-syscall`. 15 | * Add `-J` option: `--join-ns`. 16 | * Add `-O` option: `--oom-score-adj`. 17 | * Fix binfmt_misc for rootless container. 18 | * Support mount flags as prefix of source. 19 | * Support tmpfs and overlayfs as mount source. 20 | * Unset all environment variables before running container. 21 | * Fix: drop CAP_SYS_CHROOT to avoid escape. 22 | # v3.8: 23 | * Support more platforms, currently supports: arm64, armv7, armhf, riscv64, i386, loong64, s390x, ppc64le and x86_64. 24 | * Improve rootless container support. 25 | * Add `-W` option: `--work-dir`. 26 | * Add `-A` option: `--unmask-dirs`. 27 | * Add `-E` option: `--user`. 28 | * Add `-t` option: `--hostname`. 29 | * Add `-F` option: `--ruri-fetch`. 30 | * Add `-x` option: `--disable-network`. 31 | * Add `-C` option: `--correct-config`. 32 | * Add `-K` option: `--use-kvm`. 33 | * Add `-I` option: `--char-dev`. 34 | * Add `-i` option: `--hide-pid`. 35 | * Add `-T` option: `--timens-offset`. 36 | * Add `-b` option: `--background`. 37 | * Add `-L` option: `--log-file`. 38 | * `-c` option can now recognize other args and command to run. 39 | * `-U` and `-P` option can now use config file. 40 | * Rewrite cgroup support, add cpupercent limit support. 41 | * Change MAX_MOUNTPOINTS AND MAX_ENVS from 128*2 to 512*2. 42 | * Auto kill all processes in container when umounting container. 43 | * We will always mount / as the first mountpoint. 44 | * Set the size limit of /dev/shm when memory limit is set. 45 | * Fix crash on capget(). 46 | * Fix pivot_root does not work. 47 | * Fix unshare container when pivot_root failed. 48 | * Enable unshare if detected ns_pid is ruri. 49 | * Correct ns_pid for unshare container when exec command in container again. 50 | * Fix qemu path when we pre-mount the rootfs from other source. 51 | * Add autotest. 52 | * Fix memory issues. 53 | * libk2v: Add lenth check for array to avoid overflow. 54 | * libk2v: rewrite deserialization part. 55 | * cprintf: Correct the way to get buffer size, avoid overflow. 56 | * General bug fixes. 57 | # v3.7: 58 | * Ruri can now recognize [COMMAND [ARGS]...] without absolute path. 59 | * Add `-f` option: fork() before exec the command in container. 60 | * Add `-P` option: show process info. 61 | * Add `-j` option: just chroot, do not create /dev, /proc and /sys. 62 | * Fix unshare container joining ns. 63 | * Use pivot_root(2) instead of chroot(2) in unshare and rootless container. 64 | * Update masked dirs/files. 65 | * Update easter egg: `ruri AwA`. 66 | * Add debian packege info. 67 | * General bug fixes. 68 | # v3.6: 69 | * Add `uidmap` suid binary support, to fix setgroup() failed in rootless container. 70 | * ruri can now recognize CONTAINER_DIR path start without `.` or `/`. 71 | * `-q` option can now use qemu path on host. 72 | * Support architecture aliases for `-a` option (e.g. arm64=>aarch64). 73 | * General bug fixes. 74 | # v3.5: 75 | * Capability can now be controled by value. 76 | * Support mount files to container. 77 | * General bug fixes. 78 | # v3.4: 79 | * Default command wil be /bin/sh if /bin/su does not exist. 80 | * Fix mount image file. 81 | * Fix block device as root. 82 | * Fix configure script. 83 | * General bug fixes. 84 | # v3.3: 85 | * Add github action, you can now download static binary. 86 | * Fix configure script. 87 | * Fix CAP_LAST_CAP. 88 | * Update libk2v. 89 | * Add comments in configs. 90 | * General bug fixes. 91 | # v3.2: 92 | * Fix mountpoint in rootless container. 93 | * General bug fixes. 94 | # v3.1: 95 | * Make .rurienv immutable. 96 | * Drop CapAmb && Clear CapInh. 97 | * Add cgroup support for cpuset and memory. 98 | * Add `-l` option: `--limit`. 99 | * Update seccomp profile. 100 | * Add a configure script. 101 | * General bug fixes. 102 | # v3.0: 103 | * Remove daemon support. 104 | * Use libk2v for config files and .rurienv file. 105 | * Remove old `-d` `-D` `-K` `-l` `-t` `-T` option. 106 | * Add long args support. 107 | * `-d` option now aliases to `--drop`. 108 | * `-D` option now aliases to `--dump-config`. 109 | * Add `-o` option: `--output` for `-D`. 110 | * Add `-N` option: `--no-rurienv`. 111 | * Add `-R` option: `--read-only`. 112 | * Add `-M` option: `--ro-mount`. 113 | * Automatically unset $LD_PRELOAD before running container(Maybe will not work). 114 | * Fix `-U` option might also umount other mountpoints on host. 115 | * General bug fixes. 116 | # v2.7: 117 | * Catch segfault. 118 | * Fix default mountpoint. 119 | * Fix path_is_absolute(p) failed. 120 | * Remove ASAN in Makefile. 121 | * General bug fixes. 122 | # v2.6: 123 | * Fix uid_map and gid_map for `-r` option. 124 | * Auto detect mountpoint type for `-m` option. 125 | * Remove manpage && update README.md. 126 | * General bug fixes. 127 | # v2.5-hotfix2: 128 | * Fix default caplist caused `could not change the root directory` and `Failed to check chroot()` 129 | # v2.5: 130 | * Add binfmt_misc support. 131 | * Add `-a` option: Simulate architecture via binfmt_misc & QEMU. 132 | * Add `-q` option: Specify the path of QEMU. 133 | * Add `-S` option: Bind-mount /dev/, /sys/ and /proc/ from host. 134 | * General bug fixes. 135 | # v2.4: 136 | * Add `-r` option: run rootless container. 137 | * Add an easter egg: `ruri AwA`. 138 | * Rewrite Makefile, you can `make -j$(nproc)` now. 139 | * General bug fixes. 140 | # v2.3: 141 | * `-hh` => `-H`. 142 | * Add `-T` option. 143 | * Change src structure. 144 | * Rewrite most of the code. 145 | * General bug fixes. 146 | # v2.2: 147 | * Add new security options of Clang/GCC. 148 | * Disable no_new_privs bit by default for seccomp. 149 | * Disallow raising ambient capabilities via the prctl(2) PR_CAP_AMBIENT_RAISE operation. 150 | * Protect /sys/block by mounting a tmpfs on it. 151 | * Close other fds before exec(), (maybe useless). 152 | * Make dev log colorful. 153 | * General bug fixes. 154 | # v2.1: 155 | * Fix seccomp rule. 156 | * Change src structure. 157 | * Add new security options of Clang/GCC. 158 | * General bug fixes. 159 | # v2.0: 160 | * Fix compile error (Thanks @dabao1955 for reporting). 161 | # v2.0 rc2: 162 | * Fix umount() can't work. 163 | * (I think) Fix seccomp rule. 164 | # v2.0 rc1: 165 | * Remove ruri.sock after running `ruri -k`. 166 | * Tidy all the code/files. 167 | * General bug fixes. 168 | # v2.0 beta4: 169 | * Drop CAP_SYS_CHROOT and CAP_MKNOD by default 170 | * General bug fixes. 171 | # v2.0 beta3: 172 | * Add `-V` option - Show version code. 173 | * Make show_version() more beauty. 174 | * Update help page. 175 | * The code of ruri is over 3k lines now! 176 | * General bug fixes. 177 | # v2.0 beta2: 178 | * Add a manpage. 179 | * Add `-n` `-s` option. 180 | * * `-n` :Set NO_NEW_PRIVS Flag 181 | * * `-s` :Enable Seccomp 182 | * General bug fixes. 183 | # v2.0 beta: 184 | * Add `-hh` option. 185 | * General bug fixes. 186 | # v2.0 pre: 187 | * Rewrite all the code. 188 | * Add daemon support for unshare containers. 189 | * Rewrite Makefile, add clang-tidy and clang-format support. 190 | * Openrc init of alpine workd as expected on Kali-rollowing(amd64). 191 | * Add `-D` `-K` `-t` `-l` `-e` `-m` option: 192 | * * -D : Run daemon. 193 | * * -K : Kill daemon. 194 | * * -t : Check if daemon is running. 195 | * * -l : list all running unshare containers. 196 | * * -e [env] [value] : Set environment variable value. 197 | * * -m [dir] [mountpoint] : Mount dir to mountpoint. 198 | * General bug fixes. 199 | 200 | ruri only has v2.x 201 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2024 Moe-hacker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Moe Hacker -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # 3 | # 4 | # This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | # 6 | # MIT License 7 | # 8 | # Copyright (c) 2022-2024 Moe-hacker 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | # 28 | # 29 | # Premature optimization is the root of evil. 30 | # 31 | ifeq ($(wildcard config.mk),) 32 | $(error config.mk is missing. Please run 'make configure' before building) 33 | endif 34 | 35 | CCCOLOR = \033[1;38;2;254;228;208m 36 | LDCOLOR = \033[1;38;2;254;228;208m 37 | STRIPCOLOR = \033[1;38;2;254;228;208m 38 | BINCOLOR = \033[34;1m 39 | ENDCOLOR = \033[0m 40 | CC_LOG = @printf ' $(CCCOLOR)CC$(ENDCOLOR) $(BINCOLOR)%b$(ENDCOLOR)\n' 41 | LD_LOG = @printf ' $(LDCOLOR)LD$(ENDCOLOR) $(BINCOLOR)%b$(ENDCOLOR)\n' 42 | STRIP_LOG = @printf ' $(STRIPCOLOR)STRIP$(ENDCOLOR) $(BINCOLOR)%b$(ENDCOLOR)\n' 43 | CLEAN_LOG = @printf ' $(CCCOLOR)CLEAN$(ENDCOLOR) $(BINCOLOR)%b$(ENDCOLOR)\n' 44 | # Strip. 45 | STRIP = strip 46 | # Formater. 47 | FORMATER = clang-format -i 48 | SRC = src/*.c src/easteregg/*.c 49 | HEADER = src/include/*.h src/easteregg/include/*.h 50 | # Checker. 51 | CHECKER = clang-tidy 52 | CHECKER_FLAGS = --checks=*,-clang-analyzer-security.insecureAPI.strcpy,-altera-unroll-loops,-cert-err33-c,-concurrency-mt-unsafe,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-readability-function-cognitive-complexity,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-bugprone-easily-swappable-parameters,-cert-err34-c,-misc-include-cleaner,-readability-identifier-length,-bugprone-signal-handler,-cert-msc54-cpp,-cert-sig30-c,-altera-id-dependent-backward-branch,-bugprone-suspicious-realloc-usage,-hicpp-signed-bitwise,-clang-analyzer-security.insecureAPI.UncheckedReturn,-bugprone-reserved-identifier,-cert-dcl37-c,-cert-dcl51-cpp,-google-readability-function-size,-hicpp-function-size,-readability-function-size,-bugprone-reserved-identifier,-cert-dcl37-c,-cert-dcl51-cpp 53 | -include config.mk 54 | # Target. 55 | objects = easteregg/action.o easteregg/nekofeng.o easteregg/layer.o easteregg/typewriter.o caplist.o chroot.o cprintf.o info.o rurienv.o rurifetch.o seccomp.o signal.o umount.o unshare.o rootless.o mount.o k2v.o elf-magic.o config.o cgroup.o passwd.o ps.o ruri.o main.o 56 | O = out 57 | BIN_TARGET = ruri 58 | .NOTPARALLEL: 59 | .ONESHELL: 60 | .PHONY :all dev static static-bionic build_dir check format clean upk2v upcprintf help test config dbg_config dev_config static_config configure 61 | all :build_dir $(objects) 62 | @cd $(O) 63 | @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(LD_FLAGS) 64 | $(LD_LOG) $(BIN_TARGET) 65 | @$(STRIP) $(BIN_TARGET) 66 | $(STRIP_LOG) $(BIN_TARGET) 67 | @cp -f $(BIN_TARGET) ../ 68 | @cd .. && rm -rf $(O) 69 | dev :build_dir $(objects) 70 | @cd $(O) 71 | @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(LD_FLAGS) 72 | $(LD_LOG) $(BIN_TARGET) 73 | @cp -f $(BIN_TARGET) ../ 74 | @cd .. && rm -rf $(O) 75 | static :all 76 | static-bionic :all 77 | build_dir: 78 | @mkdir -p $(O) 79 | @mkdir -p $(O)/easteregg 80 | $(objects) :%.o:src/%.c $(build_dir) 81 | @cd $(O) 82 | @$(CC) $(CFLAGS) -c ../$< -o $@ 83 | $(CC_LOG) $@ 84 | check : 85 | @printf "\033[1;38;2;254;228;208mCheck list:\n" 86 | @sleep 1.5s 87 | @$(CHECKER) $(CHECKER_FLAGS) --list-checks $(SRC) -- $(DEV_CFLAGS) 88 | @printf ' \033[1;38;2;254;228;208mCHECK\033[0m \033[34;1m%b\033[0m\n' $(SRC) 89 | @$(CHECKER) $(CHECKER_FLAGS) $(SRC) -- $(COMMIT_ID) 90 | @printf ' \033[1;38;2;254;228;208mDONE.\n' 91 | configure: 92 | @bash autogen.sh && bash configure 93 | format : 94 | $(FORMATER) $(SRC) $(HEADER) 95 | shfmt -i 4 -w test/*.sh 96 | shfmt -i 4 -w test/root/*.sh 97 | shfmt -i 4 -w configure 98 | chmod 777 test/*.sh 99 | chmod 777 test/root/* 100 | clang-format -i test/*.c 101 | sed -i "s/U '/U'/g" src/easteregg/*.c 102 | sed -i "s/U \"/U\"/g" src/easteregg/*.c 103 | clang-format -i build.c 104 | clean : 105 | $(CLEAN_LOG) $(BIN_TARGET) 106 | @rm -f $(BIN_TARGET)||true 107 | $(CLEAN_LOG) $(O) 108 | @rm -rf $(O)||true 109 | $(CLEAN_LOG) peda* 110 | @rm -f peda* 111 | upk2v : 112 | cp ../libk2v/src/k2v.c src/k2v.c 113 | cp ../libk2v/src/include/k2v.h src/include/k2v.h 114 | upcprintf : 115 | cp ../cprintf/cprintf.c src/cprintf.c 116 | cp ../cprintf/include/cprintf.h src/include/cprintf.h 117 | help : 118 | @printf "\033[1;38;2;254;228;208mUsage:\n" 119 | @echo " make all compile" 120 | @echo " make dev compile, do not strip" 121 | @echo " make clean clean" 122 | @echo "Only for developers:" 123 | @echo " make check run clang-tidy" 124 | @echo " make format format code" 125 | test : 126 | cd test && $(MAKE) 127 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/rurioss/ruri/raw/main/logo/logo.png) 2 | 3 | --- 4 | 5 |

「 须臾水面明月出,沧江万顷琉璃寒 」

6 | 7 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.14021121.svg)](https://doi.org/10.5281/zenodo.14021121) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/RuriOSS/ruri) 8 | 9 | ![](https://img.shields.io/badge/backward-compatible-00d000?style=flat&labelColor=gray) 10 | ![](https://img.shields.io/badge/Powered%20By-GNU%20C-00d000?style=flat&labelColor=gray&logo=C) 11 | 12 | [![Build release](https://github.com/RuriOSS/ruri/actions/workflows/build.yml/badge.svg)](https://github.com/RuriOSS/ruri/actions/workflows/build.yml) [![Run autotest](https://github.com/RuriOSS/ruri/actions/workflows/test.yml/badge.svg)](https://github.com/RuriOSS/ruri/actions/workflows/test.yml) 13 | 14 | # About: 15 |  Not "Why not docker", but "When cannot docker". 16 |  ruri is pronounced as `luli`, or you can call it `[瑠璃/琉璃]` ~~(るり)~~ in Chinese or Japanese as well. 17 |  ruri is acronym to Lightweight, User-friendly Linux-container Implementation. 18 |  ruri is a powerful container implementation that runs on almost any Linux device, even with incomplete kernel configurations or minimal storage space. 19 | 20 | # Security Reporting: 21 | For vulnerability reporting, please refer to [SECURITY.md](SECURITY.md). 22 | 23 | # The enhanced version 24 | 25 | [rurima](https://github.com/Moe-hacker/rurima) was planned to be the ruri manager, but since it now has a full integration of ruri, you can use it as an enhanced version of ruri. 26 | 27 | # Highlights 28 | 29 | - **Powerful Features** 30 | - Supports chroot, unshare with pivot_root, capability control, cgroups, no_new_privs, environment/user/workdir setup, seccomp, and more. 31 | - Built-in binfmt_misc & QEMU for easy multi-arch containers. 32 | - Rootless mode (requires user namespaces). 33 | - Flexible mount options: mount images/partitions, set mountpoints as read-only or rw. 34 | - Config file support. 35 | 36 | - **Ultra-lightweight & Zero Dependencies** 37 | - Only optional `uidmap` needed for rootless mode; all other features are built-in. 38 | - Statically linked binaries for many architectures. 39 | - Very small binary size (even <200k with upx), yet over 30 options. 40 | 41 | - **Flexible & Cross-platform** 42 | - Runs on rooted Android, IoT, amd64, s390x, and more, just needs root. 43 | 44 | - **Secure by Design** 45 | - Rootless containers, security options, and read-only filesystem support. 46 | 47 | - **Simple for Beginners** 48 | - Can replace `chroot` directly; easy to use without learning every option. 49 | 50 |

51 | 52 |

53 | 54 | # Terms of Use: 55 | See [TERMS_OF_USE.md](TERMS_OF_USE.md) 56 | 57 | # For Android user: 58 | You need to root your phone first, ruri supports to run with root on Android devices. 59 | # Backward compatibility: 60 | We promise that ruri has backward compatibility of cli usage and config file since v3.9.0, you can keep updated to the newest version. Any breaking changes will not be introduced to v3.9.x 61 | 62 | # Important Notice: 63 | Considering the security issues of chroot, ruri will drop CAP_SYS_CHROOT by default now 64 | If you got any issues with this, please report. 65 | In newest code, ruri will also do setgroups() for root user in container to avoid permission issues on some devices, If you'd like to disable it, please use `--no-setgroups` option. 66 | 67 | # WARNING 68 | 69 | ``` 70 | * Your warranty is void. 71 | * I am not responsible for anything that may happen to your device by using this program. 72 | * You do it at your own risk and take the responsibility upon yourself. 73 | * This project is open source, you can make your own fork/rewrite but not to blame the author. 74 | * This program has no Super Cow Powers. 75 | ``` 76 | 77 | # Bug reporting 78 | 79 | If you think something does not work as expected, please [open a new isssue](https://github.com/rurioss/ruri/issues) 80 | 81 | 82 | # Asking LLM: 83 | See [Asking LLM](doc/askllm.md) for how to ask LLM about ruri. 84 | 85 | # Get ruri 86 | 87 | You can get ruri binary (statically linked) for arm64, armv7, armhf, riscv64, i386, loong64, s390x, ppc64le and x86_64 devices in [Release](https://github.com/Moe-hacker/ruri/releases/). 88 | Or you can run the following command to download ruri automatically 89 | 90 | ```sh 91 | . <(curl -sL https://get.ruri.zip/ruri) 92 | ``` 93 | 94 | This will automatically download ruri binary to `./ruri`. 95 | 96 | # Full usage: 97 | 98 | See [USAGE](doc/USAGE.md) to explore all features of ruri. 99 | 100 | # Container Security 101 | 102 | See [Enhance Container Security](doc/Security.md). 103 | 104 | # Build Manually 105 | 106 | Ruri provides statically linked binary, but if you want to build it yourself, see [Build](doc/Build.md). 107 | 108 | # Integration 109 | 110 | ruri is ready to integrate into other projects, with the MIT License, it is compatiblte to be redistribute with almost all license, or commercial/closed source. 111 | An example is ruri's own build action , it runs containers for 9 different architectures to build itself, that shows its broad application prospects. 112 | Another example is [rurima](https://github.com/Moe-hacker/rurima), I made ruri built-in for it, so it can be run as a subcommand. 113 | See [Integration](doc/Integration.md) for a guide to integrate ruri into your projects. 114 | 115 | # Behavior of rurienv 116 | 117 | After initing the container, ruri will create a file /.rurienv by default, this config can unify container config, but it will also cover some of the command-line args, you can use `--no-rurienv` to disable it, or see [rurienv.md](doc/rurienv.md) to see its behavior. 118 | You might cannot remove this file unless you run `chattr -i .rurienv`, but don't worry, after umounting conainer by `ruri -U`, this config file will be removed automatically. 119 | If you want to change the container config, just use -U to umount it and re-run the container. 120 | 121 | # FAQ 122 | 123 | [FAQ](doc/FAQ.md) 124 | 125 | # Quick start(with rurima) 126 | 127 | ## Download and unpack a rootfs 128 | 129 | ``` 130 | . <(curl -sL https://get.ruri.zip/rurima) 131 | ./rurima lxc pull -o alpine -v edge -s /tmp/alpine 132 | ``` 133 | 134 | ## Then 135 | 136 | ``` 137 | sudo ruri /tmp/alpine 138 | ``` 139 | ## Setup dns: 140 | In container: 141 | ```shell 142 | rm test/etc/resolv.conf 143 | echo nameserver 1.1.1.1|tee test/etc/resolv.conf 144 | ``` 145 | ## For unshare container 146 | 147 | ``` 148 | sudo ruri -u /tmp/alpine 149 | ``` 150 | 151 | Very simple as you can see. 152 | For command line examples, please see `ruri -H`. 153 | 154 | # Example Usage 155 | 156 | ``` 157 | # Run chroot container 158 | sudo ruri /tmp/alpine 159 | 160 | # Very simple as you can see. 161 | 162 | # About the capabilities 163 | # Run privileged chroot container 164 | sudo ruri -p /tmp/alpine 165 | 166 | # If you want to run privileged chroot container, 167 | # but you don't want to give the container cap_sys_chroot privileges 168 | sudo ruri -p -d cap_sys_chroot /tmp/alpine 169 | 170 | # If you want to run chroot container with common privileges, 171 | # but you want cap_sys_admin to be kept 172 | sudo ruri -k cap_sys_admin /tmp/alpine 173 | 174 | # About unshare 175 | # Unshare container's capability options are same with chroot. 176 | # Run unshare container 177 | sudo ruri -u /tmp/alpine 178 | 179 | # Finally, umount the container 180 | sudo ruri -U /tmp/alpine 181 | ``` 182 | # Performance 183 | 184 | On AMD Ryzen 5 5500U, Windows 11, Ubuntu 22.04.5 WSL 2 185 | 186 | ``` 187 | # uname -m 188 | x86_64 189 | # /usr/bin/time -f "Time: %E\nMax memory: %M KB" ./ruri ../t /bin/true 190 | Time: 0:00.01 191 | Max memory: 860 KB 192 | ``` 193 | 194 | ## Binary size(amd64) 195 | 196 | | | ruri | crun | % | 197 | | --------- | ---- | ---- | ------ | 198 | | (noupx) | 454K | 3.0M | -84.9% | 199 | | (withupx) | 147K | 1.3M | -88.7% | 200 | 201 | ## ~~Alphabet coverage~~ 202 | 203 | | Alphabet | ruri used | % | 204 | | -------- | --------- | --- | 205 | | 52 | 47 | 90% | 206 | 207 | # License 208 | 209 | License of code 210 | 211 | - Licensed under the MIT License 212 | - Copyright (c) 2022-2025 Moe-hacker 213 | 214 | License of clang-format config file 215 | 216 | - GPL-2.0 217 | 218 | --- 219 | 220 |

「 咲誇る花 美しく、

221 |

散り行く運命 知りながら、

222 |

僅かな時の彩を 」

223 |

(>_×)

224 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | As ruri is not a project with a large user base, there is no formal security policy in place. However, we take security seriously and encourage responsible disclosure of vulnerabilities. 3 | 4 | ## Supported Versions 5 | Only the latest v3.9.x build is supported now. As this project is backward compatible, no backports will be applied. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | As ruri is an experimental project, you can just report them whether in issues, pull requests or email moe-hacker@outlook.com 10 | 11 | # Fix and Disclosure Process: 12 | Emmmmm, when @Moe-hacker have time, he will fix the issues and release a new version. There is no formal timeline for this process. 13 | Give some time for fixing and testing. After the fix is released, the issues will be disclosed in this file. 14 | 15 | # Security Issues: 16 | ## Recent security issues and their resolutions: 17 | ### <=3.9.0: 18 | - CAP_SYS_CHROOT is kept by default, which may lead to container escape. 19 | - - Seriousness: Very High 20 | - - Fix: >=3.9.1: CAP_SYS_CHROOT is dropped by default now 21 | ### <=3.9.1: 22 | - ruri did not reload itself from memfd, which may lead to information leak or even code injection if /proc/pid/exe is replaced. 23 | - - Seriousness: Low 24 | - - Fix: >=3.9.2: ruri will reload itself from memfd now. 25 | ### <=3.9.2: 26 | - For musl based systems, if a user belongs to more than 32 groups, it will panic due to array overflow when parsing /etc/group. 27 | - - Seriousness: Medium 28 | - - Fix: >=3.9.3: This issue is fixed in v3.9.3 and later. 29 | - - Special Thanks: Thanks to @docensia, his report helps a lot. -------------------------------------------------------------------------------- /TERMS_OF_USE.md: -------------------------------------------------------------------------------- 1 | # Terms of Use 2 | 3 | # 1. License 4 | This program is released under the MIT License. The software is provided “as is”, without warranty of any kind, including but not limited to warranties of merchantability, fitness for a particular purpose, or basic usability. 5 | 6 | # 2. Third-Party Services 7 | We only provide the container runtime. We do not provide any pre-built container images or host any container registries. Users are responsible for obtaining their own container images from trusted sources. 8 | 9 | # 3. Data Handling 10 | This project does not intentionally share or upload any user data, all data processing takes place locally. 11 | 12 | 13 | # 4. Legal Compliance 14 | We strongly encourage users to comply with the applicable laws and regulations in their own jurisdictions. This project does not contain any unlawful content. Users are solely responsible for ensuring that their use of this project is lawful under the laws of their jurisdiction. 15 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SPDX-License-Identifier: MIT 3 | 4 | ################################################################################ 5 | # 6 | # Author: Moe-hacker 7 | # 8 | ################################################################################ 9 | 10 | set -e 11 | 12 | # only use the tools that are really needed, not the full build tools and features 13 | # autoreconf -fi 14 | 15 | # generate aclocal.m4 file and configure script 16 | aclocal 17 | autoconf 18 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | if [ -f /etc/resolv.conf ]; then 2 | rm /etc/resolv.conf 3 | fi 4 | echo nameserver 1.1.1.1 >/etc/resolv.conf 5 | apk update --no-cache 6 | for i in wget make clang git libseccomp-dev libseccomp-static libcap-static libcap-dev xz-dev libintl libbsd-static libsemanage-dev libselinux-utils libselinux-static xz-libs zlib zlib-static libselinux-dev linux-headers libssl3 libbsd libbsd-dev gettext-libs gettext-static gettext-dev gettext python3 build-base openssl-misc openssl-libs-static openssl zlib-dev xz-dev openssl-dev automake libtool bison flex gettext autoconf gettext sqlite sqlite-dev pcre-dev wget texinfo docbook-xsl libxslt docbook2x musl-dev gettext gettext-asprintf gettext-dbg gettext-dev gettext-doc gettext-envsubst gettext-lang gettext-libs gettext-static 7 | do 8 | if apk search -q $i >/dev/null 2>&1; then 9 | apk add $i || true 10 | fi 11 | done 12 | 13 | for package in upx lld; do 14 | if apk search -q $package >/dev/null 2>&1; then 15 | apk add $package || true 16 | fi 17 | done 18 | 19 | mkdir output output2 output3 20 | 21 | git clone --depth 1 https://github.com/moe-hacker/ruri.git 22 | cd ruri 23 | cc build.c -o build-ruri 24 | ./build-ruri -s -f 25 | 26 | cp ruri ../output/ruri 27 | cp LICENSE ../output/LICENSE 28 | 29 | cp ruri ../output2/ruri 30 | cp LICENSE ../output2/LICENSE 31 | 32 | ./build-ruri -s -c -f 33 | cp ruri ../output3/ruri 34 | cp LICENSE ../output3/LICENSE 35 | 36 | if command -v upx >/dev/null 2>&1; then 37 | cd .. 38 | upx --best output2/ruri 39 | upx --best output3/ruri 40 | fi 41 | # WTF? shell is not like rust! 42 | exit $? -------------------------------------------------------------------------------- /config.mk.in: -------------------------------------------------------------------------------- 1 | CFLAGS = @CFLAGS@ 2 | LD_FLAGS = @LD_FLAGS@ 3 | CC = @CC@ 4 | STRIP = @STRIP@ 5 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([ruri],[3.8]) 2 | AC_PROG_CC 3 | AC_CHECK_TOOL([STRIP],[strip], [true]) 4 | AC_CHECK_LIB([pthread],[pthread_create],[PTHREAD_LIB="-lpthread"],[PTHREAD_LIB=""]) 5 | AC_ARG_ENABLE([coreonly],[AS_HELP_STRING([--enable-coreonly],[Compile core only])],[enable_coreonly=yes],[enable_coreonly=no]) 6 | if test "$enable_coreonly" != "no";then 7 | AC_MSG_NOTICE([core only mode]) 8 | COREONLY_CFLAGS="-DRURI_CORE_ONLY" 9 | DISABLE_LIBCAP_CFLAGS="-DDISABLE_LIBCAP" 10 | DISABLE_LIBSECCOMP_CFLAGS="-DDISABLE_LIBSECCOMP" 11 | DISABLE_RURIENV_CFLAGS="-DDISABLE_RURIENV" 12 | LIBCAP_LIB="" 13 | LIBSECCOMP_LIB="" 14 | else 15 | COREONLY_CFLAGS="" 16 | AC_ARG_ENABLE([libcap],[AS_HELP_STRING([--disable-libcap],[Disable libcap support])],[enable_libcap=no],[enable_libcap=yes]) 17 | if test "$enable_libcap" != "no";then 18 | AC_CHECK_LIB([cap],[cap_init],[LIBCAP_LIB="-lcap"],[LIBCAP_LIB=""]) 19 | if test "$LIBCAP_LIB" == "";then 20 | AC_MSG_ERROR([libcap not found]) 21 | fi 22 | DISABLE_LIBCAP_CFLAGS="" 23 | else 24 | AC_MSG_NOTICE([libcap support disabled]) 25 | LIBCAP_LIB="" 26 | DISABLE_LIBCAP_CFLAGS="-DDISABLE_LIBCAP" 27 | fi 28 | AC_ARG_ENABLE([libseccomp],[AS_HELP_STRING([--disable-libseccomp],[Disable libseccomp support])],[enable_libseccomp=no],[enable_libseccomp=yes]) 29 | if test "$enable_libseccomp" != "no";then 30 | AC_CHECK_LIB([seccomp],[seccomp_init],[LIBSECCOMP_LIB="-lseccomp"],[LIBSECCOMP_LIB=""]) 31 | if test "$LIBSECCOMP_LIB" == "";then 32 | AC_MSG_ERROR([libseccomp not found]) 33 | fi 34 | DISABLE_LIBSECCOMP_CFLAGS="" 35 | else 36 | AC_MSG_NOTICE([libseccomp support disabled]) 37 | LIBSECCOMP_LIB="" 38 | DISABLE_LIBSECCOMP_CFLAGS="-DDISABLE_LIBSECCOMP" 39 | fi 40 | AC_ARG_ENABLE([rurienv],[AS_HELP_STRING([--disable-rurienv],[Disable .rurienv support])],[enable_rurienv=no],[enable_rurienv=yes]) 41 | if test "$enable_rurienv" != "no";then 42 | DISABLE_RURIENV_CFLAGS="" 43 | else 44 | AC_MSG_NOTICE([.rurienv support disabled]) 45 | DISABLE_RURIENV_CFLAGS="-DDISABLE_RURIENV" 46 | fi 47 | fi 48 | CFLAGS="" 49 | AC_ARG_ENABLE([static],[AS_HELP_STRING([--enable-static],[Enable static build])],[enable_static=yes],[enable_static=no]) 50 | if test "$enable_static" != "no";then 51 | AC_MSG_NOTICE([static build enabled]) 52 | STATIC_CFLAGS="-static" 53 | else 54 | STATIC_CFLAGS="" 55 | fi 56 | AC_DEFUN([CHECK_AND_ADD_CFLAGS],[AC_MSG_CHECKING([whether $CC supports $1]) 57 | CFLAGS_SAVE="$CFLAGS" 58 | if test "$enable_static" != "no";then 59 | CFLAGS="$CFLAGS $1 -static" 60 | else 61 | CFLAGS="$CFLAGS $1" 62 | fi 63 | AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], 64 | [AC_MSG_RESULT([yes]) 65 | CFLAGS="$CFLAGS_SAVE $1"], 66 | [AC_MSG_RESULT([no]) 67 | CFLAGS="$CFLAGS_SAVE"])]) 68 | AC_ARG_ENABLE([debug],[AS_HELP_STRING([--enable-debug],[Enable debug log])],[enable_debug=yes],[enable_debug=no]) 69 | if test "$enable_debug" != "no";then 70 | AC_MSG_NOTICE([debug log enabled]) 71 | DEBUG_CFLAGS="-DRURI_DEBUG" 72 | else 73 | DEBUG_CFLAGS="" 74 | fi 75 | AC_ARG_ENABLE([dev],[AS_HELP_STRING([--enable-dev],[Enable dev build])],[enable_dev=yes],[enable_dev=no]) 76 | if test "$enable_dev" != "no";then 77 | DEV_CFLAGS="-DRURI_DEV" 78 | STRIP="true" 79 | AC_MSG_NOTICE([dev build enabled]) 80 | CHECK_AND_ADD_CFLAGS([-g]) 81 | CHECK_AND_ADD_CFLAGS([-O0]) 82 | CHECK_AND_ADD_CFLAGS([-fno-omit-frame-pointer]) 83 | CHECK_AND_ADD_CFLAGS([-Wl,-z,norelro]) 84 | CHECK_AND_ADD_CFLAGS([-Wl,-z,execstack]) 85 | CHECK_AND_ADD_CFLAGS([-fno-stack-protector]) 86 | CHECK_AND_ADD_CFLAGS([-Wall]) 87 | CHECK_AND_ADD_CFLAGS([-Wextra]) 88 | CHECK_AND_ADD_CFLAGS([-pedantic]) 89 | CHECK_AND_ADD_CFLAGS([-Wconversion]) 90 | CHECK_AND_ADD_CFLAGS([-Wno-newline-eof]) 91 | CHECK_AND_ADD_CFLAGS([-Wno-unused-command-line-argument]) 92 | CHECK_AND_ADD_CFLAGS([-Wno-gnu-zero-variadic-macro-arguments]) 93 | CHECK_AND_ADD_CFLAGS([-fsanitize=address,undefined]) 94 | CHECK_AND_ADD_CFLAGS([-Wl,--build-id=sha1]) 95 | CHECK_AND_ADD_CFLAGS([-ffunction-sections]) 96 | CHECK_AND_ADD_CFLAGS([-fdata-sections]) 97 | CHECK_AND_ADD_CFLAGS([-Wl,--gc-sections]) 98 | CHECK_AND_ADD_CFLAGS([-Wl,--disable-new-dtags]) 99 | else 100 | DEV_CFLAGS="" 101 | CHECK_AND_ADD_CFLAGS([-ftrivial-auto-var-init=pattern]) 102 | CHECK_AND_ADD_CFLAGS([-fcf-protection=full]) 103 | CHECK_AND_ADD_CFLAGS([-flto=auto]) 104 | CHECK_AND_ADD_CFLAGS([-fPIE]) 105 | CHECK_AND_ADD_CFLAGS([-pie]) 106 | CHECK_AND_ADD_CFLAGS([-Wl,-z,relro]) 107 | CHECK_AND_ADD_CFLAGS([-Wl,-z,noexecstack]) 108 | CHECK_AND_ADD_CFLAGS([-Wl,-z,now]) 109 | CHECK_AND_ADD_CFLAGS([-fstack-protector-all]) 110 | CHECK_AND_ADD_CFLAGS([-fstack-clash-protection]) 111 | CHECK_AND_ADD_CFLAGS([-mshstk]) 112 | CHECK_AND_ADD_CFLAGS([-Wno-unused-result]) 113 | CHECK_AND_ADD_CFLAGS([-O2]) 114 | CHECK_AND_ADD_CFLAGS([-Wl,--build-id=sha1]) 115 | CHECK_AND_ADD_CFLAGS([-ffunction-sections]) 116 | CHECK_AND_ADD_CFLAGS([-fdata-sections]) 117 | CHECK_AND_ADD_CFLAGS([-Wl,--gc-sections]) 118 | CHECK_AND_ADD_CFLAGS([-Wl,--disable-new-dtags]) 119 | CHECK_AND_ADD_CFLAGS([-Wl,--strip-all]) 120 | CHECK_AND_ADD_CFLAGS([-U_FORTIFY_SOURCE]) 121 | CHECK_AND_ADD_CFLAGS([-D_FORTIFY_SOURCE=3]) 122 | CHECK_AND_ADD_CFLAGS([-D_FILE_OFFSET_BITS=64]) 123 | CHECK_AND_ADD_CFLAGS([-Wno-unused-command-line-argument]) 124 | fi 125 | AC_MSG_CHECKING([for commit id]) 126 | RURI_COMMIT_ID=`git rev-parse --short HEAD 2>/dev/null` 127 | if test "$RURI_COMMIT_ID" == "";then 128 | AC_MSG_RESULT([no]) 129 | RURI_COMMIT_ID="-DRURI_COMMIT_ID=\\\"Unknown\\\"" 130 | else 131 | AC_MSG_RESULT([$RURI_COMMIT_ID]) 132 | RURI_COMMIT_ID="-DRURI_COMMIT_ID=\\\"$RURI_COMMIT_ID\\\"" 133 | fi 134 | CFLAGS="$CFLAGS $RURI_COMMIT_ID $COREONLY_CFLAGS $DEBUG_CFLAGS $DEV_CFLAGS $STATIC_CFLAGS $DISABLE_LIBCAP_CFLAGS $DISABLE_LIBSECCOMP_CFLAGS $DISABLE_RURIENV_CFLAGS" 135 | LD_FLAGS="$PTHREAD_LIB $LIBCAP_LIB $LIBSECCOMP_LIB" 136 | AC_SUBST([STRIP],[$STRIP]) 137 | AC_SUBST([CC],[$CC]) 138 | AC_SUBST([CFLAGS],[$CFLAGS]) 139 | AC_SUBST([LD_FLAGS],[$LD_FLAGS]) 140 | AC_CONFIG_FILES([config.mk]) 141 | AC_OUTPUT 142 | AC_CONFIG_FILES([Makefile]) 143 | AC_MSG_NOTICE([CFLAGS: $CFLAGS]) 144 | AC_MSG_NOTICE([LD_FLAGS: $LD_FLAGS]) 145 | AC_MSG_NOTICE([configuration complete]) 146 | -------------------------------------------------------------------------------- /doc/Build.md: -------------------------------------------------------------------------------- 1 | # Dependency: 2 | libcap, libseccomp, libpthread. 3 | # Build using build.c(experimental) 4 | We are very happy to introduce a new build system for ruri: build.c 5 | It's a pure C program that does not depend on any external build system. 6 | IT IS A BIG STEP BACKWARDS THE HISTORY OF COMPUTER SCIENCE!!! 7 | That's great :) 8 | To use it, just `cc build.c` and `./a.out`. 9 | for help, see `./a.out -h`. 10 | # Build using autoconf(recommended) 11 | ``` 12 | git clone https://github.com/Moe-hacker/ruri 13 | cd ruri 14 | aclocal 15 | autoconf 16 | ./configure --enable-static 17 | make 18 | sudo cp ruri /usr/bin/ruri 19 | ``` 20 | ## NOTE: 21 | The test script has a part that must be run with `sudo`, `DO NOT` run `make test` on your devices!!!! 22 | ## Build options: 23 | ``` 24 | --enable-coreonly Compile core only 25 | --disable-libcap Disable libcap support 26 | --disable-libseccomp Disable libseccomp support 27 | --disable-rurienv Disable .rurienv support 28 | --enable-static Enable static build 29 | --enable-debug Enable debug log 30 | --enable-dev Enable dev build 31 | ``` 32 | Note: `--enable-coreonly` will auto enable `--disable-libseccomp --disable-libcap --disable-rurienv` 33 | # Build using CMake(for downstream) 34 | (if you'd prefer to use CMake) 35 | ``` 36 | git clone https://github.com/Moe-hacker/ruri 37 | cd ruri 38 | cmake . 39 | make 40 | make install 41 | ``` 42 | ## Build options in CMake: 43 | ``` 44 | -DBUILD_LIB=on Compile to shared library 45 | -DDISABLE_LIBCAP=on Disable libcap support 46 | -DDISABLE_LIBSECCOMP=on Disable libseccomp support 47 | -DDISABLE_RURIENV=on Disable .rurienv support 48 | -DENABLE_STATIC=on Enable static build 49 | -DENABLE_DEBUG=on Enable debug log 50 | ``` 51 | Note: 52 | - -DENABLE_DEBUG=on is equivalent to the traditional build options --enable-dev plus --enable-debug 53 | - When DISABLE_RURIENV and DISABLE_LIBSECCOMP and DISABLE_LIBCAP are enabled at the same time, it is equivalent to --enable-coreonly in the traditional build process 54 | 55 | ## Other target in CMake while configuration complete: 56 | ``` 57 | format Run clang-format steps 58 | strip Run strip steps 59 | tidy Run clang-tidy steps 60 | ``` 61 | -------------------------------------------------------------------------------- /doc/FAQ.md: -------------------------------------------------------------------------------- 1 | # `-P` option cannot show the process: 2 | Since ruri use pivot_root() for unshare container for better security, 3 | if your device does not support PID ns, 4 | we cannot get process info by reading the link /proc/$PID/root, because after pivot_root, it will always be `/` 5 | # Network issue: 6 | You might have `temporary failure resolving xxxxx` or `bad address xxxxx`. 7 | Try: 8 | ``` 9 | rm /etc/resolv.conf 10 | echo nameserver 1.1.1.1 > /etc/resolv.conf 11 | ``` 12 | Or, for Android, run https://github.com/Moe-hacker/daijin/raw/refs/heads/main/src/share/fixup.sh in container. 13 | # About systemd: 14 | Systemd need CAP_SYS_ADMIN to work, and need PID namespace support to make itself to be PID 1. 15 | On my device, with `sudo ./ruri -u -k cap_sys_admin ../ubuntu /sbin/init &` and then `sudo ./ruri ../ubuntu /bin/bash` to enter container, although it shows `State: degraded`, systemd seems works. 16 | But, as it might do some changes for the host and might make the device crash, you take your own risk to use it. 17 | # About container environment: 18 | For safety, ruri container is like default docker container, it will mask some directory in /sys and /proc, drop unneed capabilities, and you are not able to run command like mknod or mount by default. 19 | # About capability: 20 | ruri will set capability to the same as docker common container by default, you can use `-k [cap]` or `-d [cap]` to change the capability settings. 21 | For example, use `-d cap_sys_admin` to drop CAP_SYS_ADMIN. 22 | In fulture, maybe new caps will be added to the kernel, and their name might cannot be recognized if you are using old builds. You can use the value of cap (use `capsh --explain=[cap]` to get the value) to drop it, for example, use `-d 114` to drop the cap 114 (I don't know what the cap should be, mabe can make superuser to be a homo). 23 | # About config: 24 | Since v3.0, ruri can use [k2v](https://github.com/Moe-hacker/libk2v), a new simple config format, to store the config of a container. 25 | # About rurienv: 26 | Since v3.0, ruri removed the daemon, it use a new way to store the info of a running container. 27 | ruri will creat `/.rurienv` file into the container, to store runtime info of container. 28 | The rurienv file is automatically controled by ruri, please do not edit it. 29 | # About tty: 30 | The command `tty` in ruri might say that "not a tty". 31 | If you need to run some program like `gpg`, please use `script -q -O /dev/null` in container. 32 | # About runtime dirs: 33 | ruri will create /dev/, /sys/ and /proc/ after chroot(2) into container for better security. You can use `-S` option to force it to bind-mount system runtime dirs. 34 | # About multi-arch container: 35 | Yes, you can run multi-arch containers via ruri if your device support. 36 | It needs CONFIG_BINFMT_MISC enabled in your kernel config. 37 | You need to copy qemu-*-static to your container first. 38 | The path of qemu is the absolute path of qemu binary in the chroot container, for example, you have a qemu binary at `/path/to/container/qemu-amd64-static`, use `-a x86_64 -q /qemu-amd64-static` arguments to start the container. 39 | # About rootless container: 40 | If you get error like `Couldn't create temporary file /tmp/apt.conf.sIKx3J for passing config to apt-key` in container, please `chmod 777 /tmp`. 41 | You need uidmap installed in your host. 42 | You might need /etc/subuid and /etc/subgid configured in your host. 43 | # About Seccomp: 44 | The seccomp rule of ruri is based on Docker's default seccomp profile. ruri does not provide the way to change it, but you can edit src/seccomp.c and rewrite setup_seccomp() with your own config. -------------------------------------------------------------------------------- /doc/Integration.md: -------------------------------------------------------------------------------- 1 | ## Using ruri as container implementation: 2 | Here is a simple example, to build file command in an alpine container with ruri: 3 | ```bash 4 | # Download and unpack rootfs 5 | bash -c ". <(curl -sL https://get.ruri.zip/rurima) -s" 6 | ./rurima lxc pull -o alpine -v edge -s ./alpine 7 | # Get ruri 8 | bash -c ". <(curl -sL https://get.ruri.zip/ruri) -s" 9 | # Copy build script 10 | sudo cp build.sh alpine/build.sh 11 | sudo chmod +x alpine/build.sh 12 | # Run container 13 | sudo ./ruri ./alpine /bin/sh /build.sh 14 | ``` 15 | 16 | build.sh: 17 | ```sh 18 | # Fix DNS problem 19 | rm /etc/resolv.conf 20 | echo nameserver 1.1.1.1 > /etc/resolv.conf 21 | # Add testing source 22 | echo https://dl-cdn.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories 23 | apk add wget make clang git xz-dev libintl libbsd-static libsemanage-dev libselinux-utils libselinux-static xz-libs zlib zlib-static libselinux-dev linux-headers libssl3 libbsd libbsd-dev gettext-libs gettext-static gettext-dev gettext python3 build-base openssl-misc openssl-libs-static openssl zlib-dev xz-dev openssl-dev automake libtool bison flex gettext autoconf gettext sqlite sqlite-dev pcre-dev wget texinfo docbook-xsl libxslt docbook2x musl-dev gettext gettext-asprintf gettext-dbg gettext-dev gettext-doc gettext-envsubst gettext-lang gettext-libs gettext-static 24 | apk add upx 25 | mkdir output 26 | 27 | # build file 28 | git clone https://github.com/file/file 29 | cd file 30 | autoreconf -vif 31 | ./configure LDFLAGS="-static -Wl,--gc-sections -ffunction-sections -fdata-sections" --disable-nss --enable-static --disable-shared --disable-liblastlog2 32 | make -j8 LDFLAGS="-all-static -Wl,--gc-sections -ffunction-sections -fdata-sections" 33 | strip src/file 34 | cp src/file ../output/file-static 35 | cp magic/magic.mgc ../output/ 36 | cc -static -o file ../file.c 37 | strip file 38 | upx file 39 | cp file ../output/file 40 | cp COPYING ../output/LICENSE-file 41 | cd .. 42 | cd output 43 | tar -cvf ../$(uname -m).tar . 44 | exit 0 45 | ``` 46 | ## Using ruri config file: 47 | If you have a container in `/test`, 48 | Use `ruri -D -o test.conf /test` to dump the config into test.conf 49 | You can also add other options like `ruri -D -o test.conf -d cap_chmod -k cap_sys_admin -w -u /test /bin/sh` 50 | So next time, just use `ruri -c /path/to/test.conf` to run the container. 51 | ## integrate with source: 52 | main() has been replaced to ruri, and every funcion in ruri have ruri_ or nekofeng_ prefix now, so you need not worry about conflicts of symbols now. 53 | If your project supports, you can remove `main()` function and use ruri as a lib. But make sure that you know how ruri.c works, especially how RURI_CONTAINER struct works. 54 | An example is [rurima](https://github.com/Moe-hacker/rurima), I made ruri to be its subcommand. So `rurima r` have the same effect with `ruri`. 55 | NOTE: ruri will re-exec itself from memfd to clear environment variables and avoid security issues. So you need to call ruri_clear_env() in your program with your argv so that the re-exec can work properly. -------------------------------------------------------------------------------- /doc/Security.md: -------------------------------------------------------------------------------- 1 | # Enhance Container Security: 2 | Here are several ways to enhance the security of ruri container, kindly read this document before using ruri. 3 | ## Regular: 4 | ### Run rootless container: 5 | If your device supports user ns, you can install uidmap and use `-r` option with non-privileged user, so that you can avoid using root privileges to run the container. 6 | ### Run command in container with non-privileged user: 7 | If you can not run rootless container, there's another choice, add a non-privileged user in your container and use `-E username` to run command as non-privileged user instead root. 8 | If you don't need any privileges, it's better to enable no_new_privs at the same time. 9 | ### Hidepid: 10 | ruri supports hidepid options for /proc, use `-i 1/2` to enable it. 11 | ### Capabilities: 12 | Ruri will automatically drop unneeded capabilities, but ruri also provides capability control function, you can read capabilities(7) and use `-d` option to filter out unnecessary capabilities in container. 13 | ### Disable .rurienv: 14 | Ruri will create the file /.rurienv in container, to avoid security issues, this file is immutable and read-only. 15 | But you can also disable creating it using `-N` option. 16 | ### Set memory limit: 17 | Ruri supports memory cgroup, you can use `-l` option to set the limit of memory usage. 18 | ### Enable unshare: 19 | Ruri supports unshare, it's recommended to enable this feature for better security. 20 | Unshare container will use pivot_root(2) instead chroot(2), so it's more secure. 21 | ### Mount other mountpoints as read-only: 22 | Ruri supports using `-m` option to mount other device/img/dir into container, if you only need read access to the mountpoint, try using `-M` option to make them read-only. 23 | ## Geek: 24 | ### Seccomp: 25 | Ruri has a built-in Seccomp profile, you can enable it by using `-s` option. 26 | If this profile does not fit your needs, you can edit src/seccomp.c and write your own Seccomp rules. 27 | ### Deny syscall: 28 | If you just want to deny a syscall, you can use `-X` option, this will just set SCMP_ACT_KILL for this syscall. 29 | For example: `-X unshare -X chroot`. 30 | ## Radical: 31 | ### No new privs: 32 | Ruri supports enable no_new_privs bit by `-n` option, after enabling this, command like `sudo` will be unavailable for non-privileged user. 33 | ### Mount / as read-only: 34 | Ruri supports mounting the rootfs of container as read-only by using `-R` option, this will make all the container read-only. 35 | ### Do not create runtime dirs: 36 | /dev, /proc and /sys will not be mounted if you enable `-j` option. 37 | ### Disable network: 38 | You can use `-x` option to create a new net ns and disable network for container. 39 | # Other built-in protections: 40 | ## Devices in /dev. 41 | For /dev, ruri will only create necessary devices on it, so that block devices will always be unavailable in container, and as cap_mknod and cap_sys_admin is dropped by default, you can not escape from ruri container by modifying disk partitions. 42 | ## Masked dirs: 43 | Ruri will mask some directories with tmpfs in /proc and /sys, this can protect the host to avoid security issues. 44 | ## Capabilities: 45 | Ruri will drop unneeded capabilities by default, so that it can avoid escaping from container. -------------------------------------------------------------------------------- /doc/askllm.md: -------------------------------------------------------------------------------- 1 | It is recommended to use an LLM that can access the full source code. I tried Deepwiki; it works well, though with some minor mistakes. 2 | As ruri is not a well-known project, you should ask the LLM to: 3 | - Forget all previous conversations with you 4 | - Forget all information about other Linux container implementations 5 | - Recognize that this is a new implementation of a Linux container 6 | - Refer only to Linux man pages if additional information is needed 7 | - Answer only with the information provided in the given context 8 | - Do not output any information that is not in the context 9 | 10 | Also, copy-paste or upload the README.md and other documents in the `doc` directory to the LLM. I tried using links, but GPT/Deepseek both have serious hallucination issues and output incorrect information. 11 | If the LLM cannot answer, feel free to ask the developer in a discussion or issue. 12 | -------------------------------------------------------------------------------- /doc/init.md: -------------------------------------------------------------------------------- 1 | # Initialization of container: 2 | The design of ruri is very simple, just init the environment and then call exec() to run the command in the container. For unshare container, ruri will fork() into namwspace, so there will be a process called `ruri` on host, but chroot container will not have this behavior. 3 | 4 | ## Signal: 5 | ruri will ignore SIGTTIN and SIGTTOU signals, so that the container can run in the background without being killed by these signals. This behavior can be disabled by `--enable-tty-signals`. 6 | 7 | ## The runtime files: 8 | ### Mounts: 9 | - /sys will be mounted as sysfs. 10 | - /proc will be mounted as procfs. 11 | - /dev will be mounted as tmpfs. 12 | - /dev/pts will be mounted as devpts. 13 | - /dev/shm will be mounted as tmpfs. 14 | ### Devices: 15 | ruri will automatically create these devices in the container: 16 | ```console 17 | / # tree /dev 18 | /dev 19 | ├── console 20 | ├── fd -> /proc/self/fd 21 | ├── net 22 | │ └── tun 23 | ├── null 24 | ├── ptmx 25 | ├── pts 26 | │ └── ptmx 27 | ├── random 28 | ├── shm 29 | ├── stderr -> /proc/self/fd/2 30 | ├── stdin -> /proc/self/fd/0 31 | ├── stdout -> /proc/self/fd/1 32 | ├── tty 33 | ├── tty0 -> /dev/null 34 | ├── urandom 35 | └── zero 36 | ``` 37 | ### Masked paths: 38 | And, some path will be masked by ruri, unless `--unmask-dirs` is set, for details, see init_container() in src/chroot.c and init_rootless_container() in src/rootless.c. 39 | ### Customizable behavior: 40 | You can use `-j` option to disable mounting/creating these files. 41 | You can use `-I` option to create a custom character device in /dev. #Note: rootless container will not support this option. 42 | You can use `-m` option to mount custom source from host. 43 | ## Noteable file changes: 44 | - /.rurienv is managed by ruri, you should not try to edit or remove it. 45 | - /qemu-ruri will be created by ruri if `-q` option is set, and the path of qemu binary is on host but not in container. 46 | - /etc/mtab is a symlink to /proc/mounts, ruri will always create this file unless `-j` option is set. 47 | -------------------------------------------------------------------------------- /doc/mount.md: -------------------------------------------------------------------------------- 1 | # Mount Option 2 | 3 | `ruri` supports mounting various types of resources into the container, with advanced mount types and flags. 4 | 5 | ## Syntax 6 | 7 | ``` 8 | -m [source] [target] 9 | -M [source] [target] 10 | ``` 11 | 12 | - `-m`: Mount a resource at the specified target path. 13 | - `-M`: Same as `-m`, but enforces read-only. In the new version, you can also use the `RDONLY:` prefix instead. 14 | 15 | The target path is always interpreted relative to the container's filesystem (not the host). If the target does not exist in the container, it will be created automatically. 16 | 17 | ## Source Types 18 | 19 | Depending on the type of source, different mount strategies are applied: 20 | 21 | 1. **Directory** 22 | If the source is a directory on the host, it will be bind-mounted into the container at the target. 23 | 24 | 2. **Image File** 25 | If the source is a regular file recognized as a disk image, it will be mounted via a loop device at the target. 26 | 27 | 3. **Block Device** 28 | If the source is a block device (e.g., `/dev/sdb1`), it will be mounted directly at the target. 29 | 30 | 4. **Other Regular Files** 31 | If the source is a file that is not a recognized image, it will be bind-mounted as a file at the target. 32 | 33 | 5. **Special Mount Sources** 34 | - **tmpfs** 35 | Specify with format: 36 | ``` 37 | TMPFS:size=[size] 38 | ``` 39 | Mounts a tmpfs at the target with the given size (e.g., `TMPFS:size=100M`). 40 | Note: The size can be specified in bytes, kilobytes (K), megabytes (M), or gigabytes (G). 41 | `TMPFS:` without size defaults to kernel behavior. 42 | 43 | - **overlayfs** 44 | Specify with format: 45 | ``` 46 | OVERLAY:lowerdir=/path/to/lower,upperdir=/path/to/upper,workdir=/path/to/work 47 | ``` 48 | Mounts an overlay filesystem at the target using the specified options. 49 | - **filesystem** 50 | - **EXT4:** Mounts an ext4 filesystem at the target. 51 | - **FAT32:** Mounts a FAT32 (vfat) filesystem at the target. 52 | - **NTFS:** Mounts an NTFS filesystem at the target. 53 | - **XFS:** Mounts an XFS filesystem at the target. 54 | - **BTRFS:** Mounts a Btrfs filesystem at the target. 55 | - **EXFAT:** Mounts an exFAT filesystem at the target. 56 | - **F2FS:** Mounts an F2FS filesystem at the target. 57 | - **EROFS:** Mounts an EROFS filesystem at the target. 58 | 59 | **Example:** 60 | ``` 61 | -m EXT4:/dev/sdb1 /mnt/data 62 | ``` 63 | This mounts `/dev/sdb1` as an ext4 filesystem at `/mnt/data`. 64 | ## Behavior: 65 | For image files and block devices, if the filesystem type is not specified in prefix, ruri will attempt to auto-detect the filesystem type by trying all `nodev` filesystems in your `/proc/filesystems`. 66 | ## Mount Flags 67 | 68 | Mount flags can be set using prefixes in the source string. Prefixes are colon-separated. 69 | 70 | **Example:** 71 | ``` 72 | -m RDONLY:NOEXEC:/dev/sdb1 /mnt/disk 73 | ``` 74 | This mounts `/dev/sdb1` at `/mnt/disk` as read-only and with the `noexec` flag enabled. 75 | 76 | ### Supported Flags 77 | 78 | | Prefix | Description | 79 | |-------------|---------------------------------------------| 80 | | RDONLY | Mount read-only (same as `-M`) | 81 | | NOSUID | Do not allow set-user-ID or set-group-ID | 82 | | NOEXEC | Do not allow execution of binaries | 83 | | NODIRATIME | Do not update directory access times | 84 | | NOATIME | Do not update access times | 85 | | SYNCHRONOUS | Writes are synced immediately | 86 | | DIRSYNC | Directory updates are synchronous | 87 | | MANDLOCK | Enable mandatory locking | 88 | | RELATIME | Update access time relative to modification | 89 | | SLAVE | Make mount a slave in shared subtree | 90 | | SHARED | Make mount a shared subtree | 91 | | PRIVATE | Make mount private | 92 | | UNBINDABLE | Prevent remounting elsewhere | 93 | | SILENT | Suppress mount errors in logs (if supported)| 94 | | POSIXACL | Enable POSIX ACLs | 95 | | LAZYTIME | Delay access/modify time updates | 96 | 97 | **Notes:** 98 | - Prefixes are order-insensitive but must be placed before the source path. 99 | - If the source does not exist or cannot be recognized, the mount will fail. 100 | - To ensure container isolation and prevent security risks, custom mounting of special filesystems such as `proc`, `sysfs`, `debugfs`, or similar filesystems is not permitted. If required, you can manually modify the implementation of `mount_other_type()` in `src/mount.c`. 101 | -------------------------------------------------------------------------------- /doc/rurienv.md: -------------------------------------------------------------------------------- 1 | >WARNING: This doc may be outdated. 2 | # About rurienv: 3 | .rurienv is a config file in container, to make it safe, it's immutable by default, and as container do not have cap_linux_immutable by default, you are not able to edit or remove it in container unless you use `-k cap_linux_immutable -k cap_sys_admin` or `-p` to run container, umount it and unset immutable bit for it. 4 | It will record the following config: 5 | * The capabilty to drop. 6 | * Set NO_NEW_PRIVS bit. 7 | * Enable built-in seccomp profile. 8 | * PID owning unshare namespace. 9 | * Container ID. 10 | * Work directory. 11 | * Do not show warnings. 12 | * User to run the container. 13 | * Extra mountpoint. 14 | * Extra read-only mountpoint. 15 | * Environment variable. 16 | # Security: 17 | .rurienv is set to immutable and bind-mount as read-only on itself. That means you can never edit it in container unless you have both CAP_SYS_ADMIN(umount it) and CAP_LINUX_IMMUTABLE(unset immutable bit) in container. Thus, we can basically trust it. 18 | For rootless container, we cannot set this file to immutable, so we do not trust it. 19 | It will only change ns_pid, work_dir and user. 20 | # Its behavior: 21 | 1. Cover drop_caplist, no_new_privs, enable_seccomp, no_warnings as the config first time when init container. This can only be disabled if you don't use .rurienv file. If you want to change one of these config,you should use -U to unmount container and restart it with new args/configs. 22 | 2. Cover user and work_dir settings if they are not set in cmdline arguments. 23 | 3. Store container_id to set the container process into the same cgroup. 24 | 4. Store ns_pid to make sure unshare container in the same namespace. If ruri detected ns_pid, it will automatically enable unshare and join the namespace. 25 | 5. Store extra_mountpoint and extra_ro_mountpoint for umount container. -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuriOSS/ruri/9c61a363794e14bf67f0e6c3d47fd2940b89c6f7/logo/logo.png -------------------------------------------------------------------------------- /logo/logo.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | ''' 3 | This file is part of ruri. 4 | MIT License 5 | 6 | Copyright (c) 2022-2024 Moe-hacker 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | ''' 26 | import matplotlib.pyplot as pyplot 27 | import matplotlib.patches as patches 28 | import math 29 | # Set image parameters. 30 | figure, axes = pyplot.subplots(dpi=200, figsize=(8, 4)) 31 | pyplot.box(False) 32 | pyplot.xticks([]) 33 | pyplot.yticks([]) 34 | pyplot.subplots_adjust(bottom=0.2,top=1) 35 | axes.set_aspect(1) 36 | # Draw the sectors. 37 | draw_circle = patches.Wedge((0, 6), 6, 210, 330, fill=False, linewidth=1.5, color='#fee4d0') 38 | axes.add_artist(draw_circle) 39 | draw_circle = patches.Wedge((0, -6), 6, 30, 150, fill=False, linewidth=1.5, color='#fee4d0') 40 | axes.add_artist(draw_circle) 41 | draw_circle = patches.Wedge((math.sqrt(3) * 3, 3), 6, 150, 270, fill=False, linewidth=1.5, color='#fee4d0') 42 | axes.add_artist(draw_circle) 43 | draw_circle = patches.Wedge((math.sqrt(3) * 3 * (-1), 3), 6, 270, 390, fill=False, linewidth=1.5, color='#fee4d0') 44 | axes.add_artist(draw_circle) 45 | draw_circle = patches.Wedge((math.sqrt(3) * 3, -3), 6, 90, 210, fill=False, linewidth=1.5, color='#fee4d0') 46 | axes.add_artist(draw_circle) 47 | draw_circle = patches.Wedge((math.sqrt(3) * 3 * (-1), -3), 6, 330, 450, fill=False, linewidth=1.5, color='#fee4d0') 48 | axes.add_artist(draw_circle) 49 | # Draw the diagonals. 50 | pyplot.plot([-0.003, -0.003], [-6, 6], linewidth=1.5, color='#fee4d0') 51 | pyplot.plot([math.sqrt(3) * 3 * (-1), math.sqrt(3) * 3], [-3, 3], linewidth=1.5, color='#fee4d0') 52 | pyplot.plot([math.sqrt(3) * 3, math.sqrt(3) * 3 * (-1)], [-3, 3], linewidth=1.5, color='#fee4d0') 53 | pyplot.xlim(-8, 8) 54 | pyplot.ylim(-8, 8) 55 | # Draw the title. 56 | pyplot.title("r u r i", color='#fee4d0', y=-0.05, x=0.478, fontsize=39, fontweight="normal") 57 | pyplot.text(-0.45, -0.12, "Lightweight, User-friendly Linux-container Implementation", fontsize=13,fontweight="black", transform=axes.transAxes, color='#fee4d0') 58 | pyplot.text(-0.445, -0.21, " Revamp, Until Reach the Ideal", fontsize=13,fontweight="black", transform=axes.transAxes, color='#fee4d0') 59 | # Save as logo.png. 60 | pyplot.savefig('logo.png', transparent=True) -------------------------------------------------------------------------------- /logo/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib==3.6.3 2 | -------------------------------------------------------------------------------- /logo/rurifetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuriOSS/ruri/9c61a363794e14bf67f0e6c3d47fd2940b89c6f7/logo/rurifetch.png -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | I am very happy that you are here to read my code, 2 | and, if there's any problem, please report it to me. 3 | Plus, don't care about the easteregg, that's just pure shit. 4 | # The RURI_CONTAINER struct: 5 | All configs of a container are defined in this struct, it's a very large struct. 6 | # cprintf() and libk2v: 7 | cprintf() is the implementation of printf() with color, it's just for output. 8 | libk2v is the implementation of config file. 9 | # base function call graph: 10 | ``` 11 | main() => ruri() =>|| => other util func 12 | |- enable unshare? => ruri_run_unshare_container() => ruri_run_chroot_container() 13 | |- enable rootless? => ruri_run_rootless_container() => ruri_run_rootless_chroot_container() 14 | |- none? => ruri_run_chroot_container() 15 | ``` 16 | And, panic() will catch core signal, detect_suid_or_capability() will check if there is SUID or caps on ruri binary. -------------------------------------------------------------------------------- /src/caplist.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * This file provides functions to manage capability list. 33 | * But drop_caps() is in chroot.c, not here. 34 | * As that time I didn't learnd C well, 35 | * I didn't use a struct with length and capacities, 36 | * but used the RURI_INIT_VALUE to mark the end of the list. 37 | * And now I know that RURI_INIT_VALUE is a sentinel in fact. 38 | */ 39 | #ifndef DISABLE_LIBCAP 40 | // cap_from_name() that supports both upper and lower case. 41 | int ruri_cap_from_name(const char *str, cap_value_t *cap) 42 | { 43 | char *buf = malloc(strlen(str) + 1); 44 | for (int i = 0; str[i] != '\0'; i++) { 45 | buf[i] = (char)tolower(str[i]); 46 | } 47 | buf[strlen(str)] = '\0'; 48 | int ret = cap_from_name(buf, cap); 49 | free(buf); 50 | return ret; 51 | } 52 | #endif 53 | // Add a cap to caplist. 54 | void ruri_add_to_caplist(cap_value_t *_Nonnull list, cap_value_t cap) 55 | { 56 | /* 57 | * If cap is already in list, just do nothing and quit. 58 | * list[] is initialized by RURI_INIT_VALUE, 59 | * and the RURI_INIT_VALUE is the end of the list. 60 | */ 61 | #ifndef DISABLE_LIBCAP 62 | // We do not add non-supported capabilities to caplist. 63 | if (!CAP_IS_SUPPORTED(cap)) { 64 | return; 65 | } 66 | // Add cap to caplist. 67 | if (!ruri_is_in_caplist(list, cap)) { 68 | for (int k = 0; true; k++) { 69 | if (list[k] == RURI_INIT_VALUE) { 70 | list[k] = cap; 71 | list[k + 1] = RURI_INIT_VALUE; 72 | break; 73 | } 74 | } 75 | } 76 | #endif 77 | } 78 | // Check if the cap is in the list. 79 | bool ruri_is_in_caplist(const cap_value_t *_Nonnull list, cap_value_t cap) 80 | { 81 | /* 82 | * If cap is in list, return true, 83 | * else, return false. 84 | * RURI_INIT_VALUE is the end of the list. 85 | */ 86 | for (int i = 0; true; i++) { 87 | if (list[i] == cap) { 88 | return true; 89 | break; 90 | } 91 | if (list[i] == RURI_INIT_VALUE) { 92 | break; 93 | } 94 | } 95 | return false; 96 | } 97 | // Del a cap from caplist. 98 | void ruri_del_from_caplist(cap_value_t *_Nonnull list, cap_value_t cap) 99 | { 100 | /* 101 | * If the cap is not in list, just do nothing and quit. 102 | * Or we will delete it from the list. 103 | */ 104 | for (int i = 0; true; i++) { 105 | if (list[i] == cap) { 106 | while (i <= RURI_CAP_LAST_CAP) { 107 | list[i] = list[i + 1]; 108 | i++; 109 | } 110 | list[i - 1] = RURI_INIT_VALUE; 111 | return; 112 | } 113 | if (list[i] == RURI_INIT_VALUE) { 114 | return; 115 | } 116 | } 117 | } 118 | void ruri_build_caplist(cap_value_t caplist[], bool privileged, cap_value_t drop_caplist_extra[], cap_value_t keep_caplist_extra[]) 119 | { 120 | /* 121 | * If privileged is true, we setup a full list of all capabilities, 122 | * del keep_caplist_common[] from the list, 123 | * and then add drop_caplist_extra[] to the list, 124 | * and del keep_caplist_extra[] from the list. 125 | * 126 | * If privileged is false, we just add drop_caplist_extra[] to the list, 127 | * and del keep_caplist_extra[] from the list. 128 | * 129 | * NOTE: keep_caplist_extra[] will cover drop_caplist_extra[], 130 | * if they have the same capabilities. 131 | */ 132 | 133 | #ifndef DISABLE_LIBCAP 134 | // Based on docker's default capability set. 135 | // And I removed some unneeded capabilities. 136 | cap_value_t keep_caplist_common[] = { CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FSETID, CAP_FOWNER, CAP_SETGID, CAP_SETUID, CAP_SETFCAP, CAP_SETPCAP, CAP_NET_BIND_SERVICE, CAP_KILL, CAP_AUDIT_WRITE, RURI_INIT_VALUE }; 137 | // Set default caplist to drop. 138 | caplist[0] = RURI_INIT_VALUE; 139 | if (!privileged) { 140 | // Add all capabilities to caplist. 141 | for (int i = 0; CAP_IS_SUPPORTED(i); i++) { 142 | caplist[i] = i; 143 | caplist[i + 1] = RURI_INIT_VALUE; 144 | } 145 | // Del keep_caplist_common[] from caplist. 146 | for (int i = 0; true; i++) { 147 | if (keep_caplist_common[i] == RURI_INIT_VALUE) { 148 | break; 149 | } 150 | ruri_del_from_caplist(caplist, keep_caplist_common[i]); 151 | } 152 | } 153 | // Add drop_caplist_extra[] to caplist. 154 | if (drop_caplist_extra[0] != RURI_INIT_VALUE) { 155 | for (int i = 0; true; i++) { 156 | if (drop_caplist_extra[i] == RURI_INIT_VALUE) { 157 | break; 158 | } 159 | ruri_add_to_caplist(caplist, drop_caplist_extra[i]); 160 | } 161 | } 162 | // Del keep_caplist_extra[] from caplist. 163 | if (keep_caplist_extra[0] != RURI_INIT_VALUE) { 164 | for (int i = 0; true; i++) { 165 | if (keep_caplist_extra[i] == RURI_INIT_VALUE) { 166 | break; 167 | } 168 | ruri_del_from_caplist(caplist, keep_caplist_extra[i]); 169 | } 170 | } 171 | #else 172 | caplist[0] = RURI_INIT_VALUE; 173 | #endif 174 | } 175 | -------------------------------------------------------------------------------- /src/easteregg/README.md: -------------------------------------------------------------------------------- 1 | # Note: 2 | This is just an easter egg, just for fun, and it will not affect any functions of ruri. 3 | I know the code is too shit, but, it works, nothing to change, and all files here are wrapped with `// NOLINTBEGIN` and `// NOLINTEND`. -------------------------------------------------------------------------------- /src/easteregg/action.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/nekofeng.h" 31 | // NOLINTBEGIN 32 | /* 33 | * This file defines the actions of the animation. 34 | * All the functions have the same arguments. 35 | */ 36 | #ifndef RURI_CORE_ONLY 37 | void nekofeng_face(useconds_t inr, unsigned int keep) 38 | { 39 | struct NEKOFENG_ACTION *action = NULL; 40 | action = nekofeng_add_action(action, -17, -9, 41 | U"\033[0m ██ ██\n" 42 | "\033[0m ██ ██ ██ ██\n" 43 | "\033[0m ██ ████ ██ ██\n" 44 | "\033[0m ██ ██ ████ ██\n" 45 | "\033[0m ██ ██████████████████ ██\n" 46 | "\033[0m ██ ██ ██ ██\n" 47 | "\033[0m ██ ██ ████ ██\n" 48 | "\033[0m ████████ ██ ██ ████ ██\n" 49 | "\033[0m ██ ██ ██ ██ ██ ██ ██ ██\n" 50 | "\033[0m ████ ██ ██ ████ ██ ██ ██ ██████\n" 51 | "\033[0m ██ ██ ██ ██ ████ ██ ██ ████████\n" 52 | "\033[0m ██ ██ ██ ████\n" 53 | "\033[0m ██ ██ ██\n" 54 | "\033[0m ████ ██ ██\n" 55 | "\033[0m ██ ██ ██\n" 56 | "\033[0m ██ ██ ██\n" 57 | "\033[0m ██ ██ ██\n" 58 | "\033[0m ████ ██ ██ ██\n" 59 | "\033[0m ████ ██████ ██\n" 60 | "\033[0m ██████ ████ ████\n" 61 | "\033[0m ████ ████\n" 62 | "\033[0m ████████████████████\n"); 63 | nekofeng_play_action(action, inr, keep); 64 | nekofeng_free_action(action); 65 | } 66 | void nekofeng_mouth(useconds_t inr, unsigned int keep) 67 | { 68 | struct NEKOFENG_ACTION *action = NULL; 69 | action = nekofeng_add_action(action, 10, 8, 70 | U"\n" 71 | "\n" 72 | " ███\n"); 73 | action = nekofeng_add_action(action, 10, 8, 74 | U"\n" 75 | "\n" 76 | " ████\n"); 77 | action = nekofeng_add_action(action, 10, 8, 78 | U"\n" 79 | "\n" 80 | " ██████\n"); 81 | action = nekofeng_add_action(action, 10, 8, 82 | U"\n" 83 | "██ ██\n" 84 | " ████\n"); 85 | nekofeng_play_action(action, inr, keep); 86 | nekofeng_free_action(action); 87 | } 88 | void nekofeng_ahoge(useconds_t inr, unsigned int keep) 89 | { 90 | struct NEKOFENG_ACTION *action = NULL; 91 | action = nekofeng_add_action(action, 6, -8, 92 | U" ██\n" 93 | " ██\n" 94 | " ██\n"); 95 | action = nekofeng_add_action(action, 6, -8, 96 | U" ██\n" 97 | " ██\n" 98 | " ██\n"); 99 | action = nekofeng_add_action(action, 6, -8, 100 | U" ██\n" 101 | " ██\n" 102 | " ██\n"); 103 | action = nekofeng_add_action(action, 6, -8, 104 | U" ██\n" 105 | " ██\n" 106 | " ██\n"); 107 | action = nekofeng_add_action(action, 6, -8, 108 | U"██\n" 109 | " ██\n" 110 | " ██\n"); 111 | action = nekofeng_add_action(action, 6, -8, 112 | U" ██\n" 113 | " ██\n" 114 | " ██\n"); 115 | nekofeng_play_action(action, inr, keep); 116 | nekofeng_free_action(action); 117 | } 118 | void nekofeng_blink_lefteye(useconds_t inr, unsigned int keep) 119 | { 120 | struct NEKOFENG_ACTION *action = NULL; 121 | action = nekofeng_add_action(action, 1, 2, 122 | U" ██████ \n" 123 | "██ ██\n" 124 | " ██████\n" 125 | " ██ ██\n" 126 | " ██████\n"); 127 | action = nekofeng_add_action(action, 1, 2, 128 | U"\n" 129 | " ██████ \n" 130 | "██ ██\n" 131 | " ██ ██\n" 132 | " ██████\n"); 133 | action = nekofeng_add_action(action, 1, 2, 134 | U"\n\n" 135 | " ██████ \n" 136 | "██ ██\n" 137 | " ██████\n"); 138 | action = nekofeng_add_action(action, 1, 2, 139 | U"\n\n\n" 140 | " ██████ \n" 141 | "██████████\n"); 142 | action = nekofeng_add_action(action, 1, 2, 143 | U"\n\n" 144 | " ████\n" 145 | " ██\n" 146 | " █████\n"); 147 | action = nekofeng_add_action(action, 1, 2, 148 | U"\n" 149 | " ████\n" 150 | " ██\n" 151 | " ██\n" 152 | " ██████\n"); 153 | nekofeng_play_action(action, inr, keep); 154 | nekofeng_free_action(action); 155 | } 156 | void nekofeng_blink_righteye(useconds_t inr, unsigned int keep) 157 | { 158 | struct NEKOFENG_ACTION *action = NULL; 159 | action = nekofeng_add_action(action, 16, 2, 160 | U" ██████ \n" 161 | "██ ██\n" 162 | " \033[31m██ ██\n" 163 | " ██ \n" 164 | " ██ ██\n"); 165 | action = nekofeng_add_action(action, 16, 2, 166 | U"\n" 167 | " ██████ \n" 168 | "██\033[31m██ ██\033[0m██\n" 169 | " \033[31m██\n" 170 | " ██ ██\n"); 171 | action = nekofeng_add_action(action, 16, 2, 172 | U"\n\n" 173 | " ██████ \n" 174 | "██ \033[31m██\033[0m ██\n" 175 | " \033[31m██ ██\n"); 176 | action = nekofeng_add_action(action, 16, 2, 177 | U"\n\n\n" 178 | " ██████ \n" 179 | "██\033[31m██ ██\033[0m██ \n"); 180 | action = nekofeng_add_action(action, 16, 2, 181 | U"\n\n" 182 | " ████\n" 183 | "██\n" 184 | " █████\n"); 185 | action = nekofeng_add_action(action, 16, 2, 186 | U"\n" 187 | " ████\n" 188 | " ██\n" 189 | "██\n" 190 | " ██████\n"); 191 | nekofeng_play_action(action, inr, keep); 192 | nekofeng_free_action(action); 193 | } 194 | #endif 195 | // NOLINTEND -------------------------------------------------------------------------------- /src/easteregg/include/nekofeng.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | // NOLINTBEGIN 31 | #define _GNU_SOURCE 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | // This program should be compiled with -lpthread. 48 | #include 49 | #ifndef _Nullable 50 | #define _Nullable 51 | #endif 52 | #ifndef _Nonnull 53 | #define _Nonnull 54 | #endif 55 | // WARNING: magic number! Do not change! 56 | #define NEKOFENG_X_SIZE 36 57 | #define NEKOFENG_Y_SIZE 2 58 | struct NEKOFENG_LAYER { 59 | /* 60 | * This is the smallest unit of the animation. 61 | * It represents one frame of an action. 62 | * The name is taken by historical reasons. 63 | */ 64 | // The string to be printed. 65 | char32_t *layer; 66 | // The offset of the x-axis. 67 | int x_offset; 68 | // The offset of the y-axis. 69 | int y_offset; 70 | }; 71 | struct NEKOFENG_ACTION { 72 | /* 73 | * This is a doubly linked list, 74 | * because we want to playback the action. 75 | */ 76 | // The layer to be printed. 77 | struct NEKOFENG_LAYER *layer; 78 | // The pointer to the last action. 79 | struct NEKOFENG_ACTION *prior; 80 | // The pointer to the next action. 81 | struct NEKOFENG_ACTION *next; 82 | }; 83 | // The offset of the x-axis. 84 | extern int nekofeng_x; 85 | // The offset of the y-axis. 86 | extern int nekofeng_y; 87 | // The lock for multi-threading. 88 | // This lock is used for print_layer() and clear_layer(). 89 | extern atomic_flag nekofeng_lock; 90 | // Function list. 91 | void nekofeng_spin_lock(atomic_flag *_Nonnull lock); 92 | void nekofeng_spin_unlock(atomic_flag *_Nonnull lock); 93 | void nekofeng_play_action(struct NEKOFENG_ACTION *_Nonnull action, useconds_t inr, unsigned int keep); 94 | void nekofeng_playback_action(struct NEKOFENG_ACTION *_Nonnull action, useconds_t inr, unsigned int keep); 95 | void nekofeng_free_action(struct NEKOFENG_ACTION *_Nonnull action); 96 | struct NEKOFENG_ACTION *nekofeng_add_action(struct NEKOFENG_ACTION *_Nonnull action, int x_offset, int y_offset, char32_t *_Nonnull layer); 97 | void nekofeng_face(useconds_t inr, unsigned int keep); 98 | void nekofeng_mouth(useconds_t inr, unsigned int keep); 99 | void nekofeng_ahoge(useconds_t inr, unsigned int keep); 100 | void nekofeng_blink_lefteye(useconds_t inr, unsigned int keep); 101 | void nekofeng_blink_righteye(useconds_t inr, unsigned int keep); 102 | void nekofeng_typewrite_layer(struct NEKOFENG_LAYER *_Nonnull layer, useconds_t inr, bool blink); 103 | void nekofeng_clear_typewrite_layer(struct NEKOFENG_LAYER *_Nonnull layer, useconds_t inr); 104 | size_t nekofeng_strlen(const char32_t *_Nonnull str); 105 | char32_t *nekofeng_strdup(const char32_t *_Nonnull str); 106 | // NOLINTEND -------------------------------------------------------------------------------- /src/easteregg/layer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/nekofeng.h" 31 | // NOLINTBEGIN 32 | #ifndef RURI_CORE_ONLY 33 | /* 34 | * This file provides the function 35 | * nekofeng_add_action(), nekofeng_play_action(), nekofeng_playback_action(), and nekofeng_free_action(). 36 | * 37 | * Usage: 38 | * 39 | * struct NEKOFENG_ACTION *action = NULL; 40 | * action = nekofeng_add_action(action, 11, 4, 41 | * "\n\033[0m vedvhfkhbfuweh \n\n"); 42 | * action = nekofeng_add_action(action, 11, 4, 43 | * "\n\033[0m vedvhfkhbfuweh \n\n"); 44 | * nekofeng_play_action(action, inr, keep); 45 | * nekofeng_playback_action(action, inr, keep); 46 | * nekofeng_free_action(action); 47 | * 48 | * Do not care how it works, because I forgot, 49 | * and I am too lazy to read the code. 50 | * 51 | */ 52 | static void clear_layer(struct NEKOFENG_LAYER *_Nonnull layer) 53 | { 54 | nekofeng_spin_lock(&nekofeng_lock); 55 | int y_offset = 0; 56 | int x_offset = 0; 57 | printf("\033[%dH", nekofeng_y + layer->y_offset); 58 | printf("\033[%dG", nekofeng_x + layer->x_offset); 59 | for (size_t i = 0; i < nekofeng_strlen(layer->layer); i++) { 60 | // The next line. 61 | if (layer->layer[i] == U'\n') { 62 | y_offset++; 63 | x_offset = 0; 64 | printf("\033[%dH", nekofeng_y + layer->y_offset + y_offset); 65 | printf("\033[%dG", nekofeng_x + layer->x_offset); 66 | continue; 67 | } 68 | // Color. 69 | if (layer->layer[i] == U'\033') { 70 | for (size_t j = i; j < nekofeng_strlen(layer->layer); j++) { 71 | if (layer->layer[j] == U'm') { 72 | i = j; 73 | break; 74 | } 75 | } 76 | continue; 77 | } 78 | // Skip space. 79 | if (layer->layer[i] != U' ') { 80 | printf("\033[%dG", nekofeng_x + layer->x_offset + x_offset); 81 | printf(" "); 82 | } 83 | x_offset++; 84 | } 85 | fflush(stdout); 86 | nekofeng_spin_unlock(&nekofeng_lock); 87 | usleep(10000); 88 | } 89 | static void print_layer(struct NEKOFENG_LAYER *_Nonnull layer) 90 | { 91 | nekofeng_spin_lock(&nekofeng_lock); 92 | int y_offset = 0; 93 | printf("\033[%dH", nekofeng_y + layer->y_offset); 94 | printf("\033[%dG", nekofeng_x + layer->x_offset); 95 | for (size_t i = 0; i < nekofeng_strlen(layer->layer); i++) { 96 | // The next line. 97 | if (layer->layer[i] == U'\n') { 98 | y_offset++; 99 | printf("\033[%dH", nekofeng_y + layer->y_offset + y_offset); 100 | printf("\033[%dG", nekofeng_x + layer->x_offset); 101 | continue; 102 | } 103 | // '#' means a ' ' to cover the layer under it. 104 | if (layer->layer[i] == U'#') { 105 | printf(" "); 106 | } // Skip space. 107 | else if (layer->layer[i] != U' ') { 108 | char character[64] = { '\0' }; 109 | mbstate_t state = { 0 }; 110 | size_t len = c32rtomb(character, layer->layer[i], &state); 111 | if (len == (size_t)-1) { 112 | perror("c32rtomb"); 113 | nekofeng_spin_unlock(&nekofeng_lock); 114 | return; 115 | } 116 | character[len] = '\0'; 117 | printf("%s", character); 118 | } else { 119 | printf("\033[1C\033[?25l"); 120 | } 121 | } 122 | printf("\033[0m"); 123 | fflush(stdout); 124 | nekofeng_spin_unlock(&nekofeng_lock); 125 | usleep(10000); 126 | } 127 | void nekofeng_play_action(struct NEKOFENG_ACTION *_Nonnull action, useconds_t inr, unsigned int keep) 128 | { 129 | struct NEKOFENG_ACTION **p = &action; 130 | while ((*p) != NULL) { 131 | print_layer((*p)->layer); 132 | usleep(inr); 133 | if ((*p)->next == NULL) { 134 | sleep(keep); 135 | } 136 | clear_layer((*p)->layer); 137 | p = &((*p)->next); 138 | } 139 | } 140 | void nekofeng_playback_action(struct NEKOFENG_ACTION *_Nonnull action, useconds_t inr, unsigned int keep) 141 | { 142 | struct NEKOFENG_ACTION **p = &action; 143 | while ((*p)->next != NULL) { 144 | p = &((*p)->next); 145 | } 146 | while ((*p) != NULL) { 147 | print_layer((*p)->layer); 148 | usleep(inr); 149 | if ((*p)->prior == NULL) { 150 | sleep(keep); 151 | } 152 | clear_layer((*p)->layer); 153 | p = &((*p)->prior); 154 | } 155 | } 156 | void nekofeng_free_action(struct NEKOFENG_ACTION *_Nonnull action) 157 | { 158 | struct NEKOFENG_ACTION *p = action; 159 | while (p != NULL) { 160 | struct NEKOFENG_ACTION *t = p; 161 | p = p->next; 162 | free(t->layer->layer); 163 | free(t->layer); 164 | free(t); 165 | } 166 | } 167 | struct NEKOFENG_ACTION *nekofeng_add_action(struct NEKOFENG_ACTION *_Nonnull action, int x_offset, int y_offset, char32_t *_Nonnull layer) 168 | { 169 | struct NEKOFENG_ACTION **p = &action; 170 | struct NEKOFENG_ACTION *prior = action; 171 | while (*p != NULL) { 172 | prior = *p; 173 | p = &((*p)->next); 174 | } 175 | (*p) = malloc(sizeof(struct NEKOFENG_ACTION)); 176 | (*p)->layer = malloc(sizeof(struct NEKOFENG_LAYER)); 177 | (*p)->layer->x_offset = x_offset; 178 | (*p)->layer->y_offset = y_offset; 179 | (*p)->layer->layer = nekofeng_strdup(layer); 180 | (*p)->next = NULL; 181 | (*p)->prior = prior; 182 | return action; 183 | } 184 | #endif 185 | // NOLINTEND -------------------------------------------------------------------------------- /src/easteregg/nekofeng.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/nekofeng.h" 31 | // NOLINTBEGIN 32 | /* 33 | * This file was the main.c in nekofeng project. 34 | * Now, it provides the function ruri_AwA() as the easteregg of ruri. 35 | * 36 | * Note: 37 | * This part of code is just for fun, do not take it seriously. 38 | * It will not affect the main function of ruri. 39 | * And, it works on my machine! 40 | */ 41 | #ifndef RURI_CORE_ONLY 42 | // The global variables are defined here. 43 | int nekofeng_x; 44 | int nekofeng_y; 45 | atomic_flag nekofeng_lock = ATOMIC_FLAG_INIT; 46 | atomic_flag nekofeng_lock2 = ATOMIC_FLAG_INIT; 47 | // The spin lock. 48 | void nekofeng_spin_lock(atomic_flag *_Nonnull l) 49 | { 50 | while (atomic_flag_test_and_set(l)) { 51 | } 52 | } 53 | // The spin unlock. 54 | void nekofeng_spin_unlock(atomic_flag *_Nonnull l) 55 | { 56 | atomic_flag_clear(l); 57 | } 58 | // The function to get the length of a string. 59 | size_t nekofeng_strlen(const char32_t *_Nonnull str) 60 | { 61 | size_t len = 0; 62 | while (str[len] != U'\0') { 63 | len++; 64 | } 65 | return len; 66 | } 67 | // The function to duplicate a string. 68 | char32_t *nekofeng_strdup(const char32_t *_Nonnull str) 69 | { 70 | size_t len = nekofeng_strlen(str); 71 | char32_t *dup = malloc((len + 1) * sizeof(char32_t)); 72 | if (dup == NULL) { 73 | return NULL; 74 | } 75 | for (size_t i = 0; i < len; i++) { 76 | dup[i] = str[i]; 77 | } 78 | dup[len] = U'\0'; 79 | return dup; 80 | } 81 | // init() function for getting window size. 82 | static void init(void) 83 | { 84 | printf("\033[H\033[2J"); 85 | // Use ioctl(2) to get the window size. 86 | struct winsize size; 87 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); 88 | if (size.ws_col < 70 || size.ws_row < 24) { 89 | printf("\033[31mThe window size is too small.\n"); 90 | exit(1); 91 | } 92 | nekofeng_x = size.ws_col / 2 - NEKOFENG_X_SIZE / 2; 93 | nekofeng_y = size.ws_row / 2 - NEKOFENG_Y_SIZE / 2; 94 | } 95 | static long nekofeng_tids[6] = { -114 }; 96 | static void update_tids(void) 97 | { 98 | long tid = syscall(SYS_gettid); 99 | nekofeng_spin_lock(&nekofeng_lock2); 100 | for (int i = 0; i < 6; i++) { 101 | if (nekofeng_tids[i] < 0) { 102 | nekofeng_tids[i] = tid; 103 | nekofeng_tids[i + 1] = -114; 104 | nekofeng_spin_unlock(&nekofeng_lock2); 105 | return; 106 | } 107 | } 108 | } 109 | static void *test0(void *arg) 110 | { 111 | update_tids(); 112 | nekofeng_face(100000, 7); 113 | return arg; 114 | } 115 | static void *test1(void *arg) 116 | { 117 | update_tids(); 118 | nekofeng_blink_lefteye(200000, 7); 119 | return arg; 120 | } 121 | static void *test2(void *arg) 122 | { 123 | update_tids(); 124 | nekofeng_blink_righteye(200000, 7); 125 | return arg; 126 | } 127 | static void *test3(void *arg) 128 | { 129 | update_tids(); 130 | nekofeng_mouth(200000, 7); 131 | return arg; 132 | } 133 | static void *test4(void *arg) 134 | { 135 | update_tids(); 136 | for (int i = 0; i < 11; i++) { 137 | nekofeng_ahoge(300000, 0); 138 | } 139 | return arg; 140 | } 141 | void ruri_AwA(void) 142 | { 143 | printf("\033[?25l"); 144 | printf("\033[?1049h"); 145 | fflush(stdout); 146 | // Maybe this is more secure? 147 | if (geteuid() == 0) { 148 | setgid(65534); 149 | setuid(65534); 150 | } 151 | // If we didn't set this, c32rtomb() will not work. 152 | setlocale(LC_ALL, ""); 153 | init(); 154 | struct NEKOFENG_LAYER layer; 155 | layer.layer = U"\033[1;38;2;254;228;208m\n" 156 | " Keep moe.\n" 157 | " Keep cool.\n" 158 | " Keep hacking.\n" 159 | "Keep on the side of technology.\n\n" 160 | " But talk is cheap,\n" 161 | " Show me the code.\n"; 162 | layer.x_offset = 3; 163 | layer.y_offset = -2; 164 | nekofeng_typewrite_layer(&layer, 50000, true); 165 | sleep(2); 166 | nekofeng_clear_typewrite_layer(&layer, 20000); 167 | // It works with bug, don't care about it. 168 | pid_t pid = fork(); 169 | if (pid > 0) { 170 | wait(NULL); 171 | } else { 172 | pthread_t t0, t1, t2, t3, t4; 173 | pthread_create(&t0, NULL, test0, NULL); 174 | pthread_create(&t3, NULL, test3, NULL); 175 | pthread_create(&t1, NULL, test1, NULL); 176 | pthread_create(&t2, NULL, test2, NULL); 177 | pthread_create(&t4, NULL, test4, NULL); 178 | sleep(7); 179 | printf("\033[H\033[2J"); 180 | for (int i = 0; i < 6; i++) { 181 | if (nekofeng_tids[i] > 0) { 182 | syscall(SYS_tgkill, getpid(), nekofeng_tids[i], SIGKILL); 183 | } else { 184 | break; 185 | } 186 | } 187 | } 188 | printf("\033[H\033[2J"); 189 | layer.layer = U"\033[1;38;2;254;228;208m\n\n" 190 | "●●●● ● ● ●●●● ●●●\n" 191 | "● ● ● ● ● ● ●\n" 192 | "●●●● ● ● ●●●● ●\n" 193 | "● ● ● ● ● ● ●\n" 194 | "● ● ●●● ● ● ●●●\n"; 195 | layer.x_offset = 7; 196 | layer.y_offset = -2; 197 | nekofeng_typewrite_layer(&layer, 5000, false); 198 | sleep(4); 199 | printf("\033[H\033[2J"); 200 | printf("\033[?1049l"); 201 | printf("\033[?25h"); 202 | fflush(stdout); 203 | exit(EXIT_SUCCESS); 204 | } 205 | #else 206 | #include "../include/ruri.h" 207 | void ruri_AwA() 208 | { 209 | cprintf("{red}ruri was build with core-only mode QwQ.\n"); 210 | } 211 | #endif 212 | // NOLINTEND -------------------------------------------------------------------------------- /src/easteregg/typewriter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/nekofeng.h" 31 | // NOLINTBEGIN 32 | /* 33 | * This file provides the function 34 | * nekofeng_typewrite_layer() and nekofeng_clear_typewrite_layer(). 35 | * 36 | * Usage: 37 | * 38 | * struct NEKOFENG_LAYER layer; 39 | * layer.layer = "\033[1;38;2;254;228;208mxxxxxxxxxxxx\n"; 40 | * layer.x_offset = 1; 41 | * layer.y_offset = 14; 42 | * nekofeng_typewrite_layer(&layer, 50000, true); 43 | * sleep(2); 44 | * nekofeng_clear_typewrite_layer(&layer, 50000); 45 | * 46 | * Do not care how it works, because I forgot, 47 | * and I am too lazy to read the code. 48 | * 49 | */ 50 | #ifndef RURI_CORE_ONLY 51 | static int get_last_line_size(char32_t *_Nonnull buf) 52 | { 53 | if (buf == NULL) { 54 | return 0; 55 | } 56 | char32_t *tmp = NULL; 57 | int ret = 0; 58 | for (size_t i = nekofeng_strlen(buf); i > 0; i--) { 59 | if (buf[i] == U'\n') { 60 | tmp = nekofeng_strdup(&buf[i + 1]); 61 | break; 62 | } 63 | } 64 | for (size_t i = 0; i < nekofeng_strlen(tmp); i++) { 65 | // Color. 66 | if (tmp[i] == U'\033') { 67 | for (size_t j = i; j < nekofeng_strlen(tmp); j++) { 68 | if (tmp[j] == U'm') { 69 | i = j; 70 | break; 71 | } 72 | } 73 | continue; 74 | } 75 | ret++; 76 | } 77 | free(tmp); 78 | return ret; 79 | } 80 | static char32_t *del_last_line(char32_t *_Nonnull buf) 81 | { 82 | if (buf == NULL) { 83 | return NULL; 84 | } 85 | char32_t *tmp = nekofeng_strdup(buf); 86 | for (size_t i = nekofeng_strlen(buf); i > 0; i--) { 87 | if (buf[i] == U'\n') { 88 | tmp[i] = U'\0'; 89 | break; 90 | } 91 | } 92 | free(buf); 93 | return tmp; 94 | } 95 | static int get_lines(char32_t *_Nonnull buf) 96 | { 97 | int j = 0; 98 | for (size_t i = 0; i < nekofeng_strlen(buf); i++) { 99 | if (buf[i] == U'\n') { 100 | j++; 101 | } 102 | } 103 | return j; 104 | } 105 | static void nekofeng_printf(char32_t *_Nonnull str) 106 | { 107 | nekofeng_spin_lock(&nekofeng_lock); 108 | for (size_t i = 0; i < nekofeng_strlen(str); i++) { 109 | char character[64] = { '\0' }; 110 | mbstate_t state = { 0 }; 111 | size_t len = c32rtomb(character, str[i], &state); 112 | if (len == (size_t)-1) { 113 | perror("c32rtomb"); 114 | nekofeng_spin_unlock(&nekofeng_lock); 115 | return; 116 | } 117 | character[len] = '\0'; 118 | printf("%s", character); 119 | } 120 | fflush(stdout); 121 | nekofeng_spin_unlock(&nekofeng_lock); 122 | } 123 | void nekofeng_typewrite_layer(struct NEKOFENG_LAYER *_Nonnull layer, useconds_t inr, bool blink) 124 | { 125 | int y_offset = 0; 126 | printf("\033[%dH", nekofeng_y + layer->y_offset); 127 | printf("\033[%dG", nekofeng_x + layer->x_offset); 128 | fflush(stdout); 129 | for (size_t i = 0; i < nekofeng_strlen(layer->layer); i++) { 130 | // The next line. 131 | if (layer->layer[i] == U'\n') { 132 | y_offset++; 133 | printf("\033[%dH", nekofeng_y + layer->y_offset + y_offset); 134 | printf("\033[%dG", nekofeng_x + layer->x_offset); 135 | continue; 136 | } 137 | // '#' means a ' ' to cover the layer under it. 138 | if (layer->layer[i] == U'#') { 139 | printf(" "); 140 | } // Skip space. 141 | else if (layer->layer[i] != U' ') { 142 | // Color. 143 | if (layer->layer[i] == U'\033') { 144 | // Be mindful! This might overflow. 145 | char32_t tmp[1024] = { U'\0' }; 146 | for (size_t j = i; j < nekofeng_strlen(layer->layer); j++) { 147 | tmp[j - i] = layer->layer[j]; 148 | tmp[j - i + 1] = 0; 149 | if (layer->layer[j] == U'm') { 150 | i = j; 151 | nekofeng_printf(tmp); 152 | break; 153 | } 154 | } 155 | continue; 156 | } 157 | if (blink) { 158 | printf("#"); 159 | fflush(stdout); 160 | usleep(inr / 8); 161 | printf("\033[1D\033[?25l"); 162 | printf("£"); 163 | fflush(stdout); 164 | usleep(inr / 8); 165 | printf("\033[1D\033[?25l"); 166 | printf("&"); 167 | fflush(stdout); 168 | usleep(inr / 8); 169 | printf("\033[1D\033[?25l"); 170 | printf("*"); 171 | fflush(stdout); 172 | usleep(inr / 8); 173 | printf("\033[1D\033[?25l"); 174 | } 175 | char character[64] = { '\0' }; 176 | mbstate_t state = { 0 }; 177 | size_t len = c32rtomb(character, layer->layer[i], &state); 178 | if (len == (size_t)-1) { 179 | perror("c32rtomb"); 180 | nekofeng_spin_unlock(&nekofeng_lock); 181 | return; 182 | } 183 | character[len] = '\0'; 184 | printf("%s", character); 185 | fflush(stdout); 186 | usleep(inr); 187 | } else { 188 | printf("\033[1C\033[?25l"); 189 | } 190 | } 191 | } 192 | void nekofeng_clear_typewrite_layer(struct NEKOFENG_LAYER *_Nonnull layer, useconds_t inr) 193 | { 194 | int y_offset = get_lines(layer->layer); 195 | char32_t *buf = nekofeng_strdup(layer->layer); 196 | int x_offset = 0; 197 | for (int i = y_offset; i > 0; i--) { 198 | x_offset = get_last_line_size(buf); 199 | buf = del_last_line(buf); 200 | for (int j = x_offset; j > 0; j--) { 201 | printf("\033[%dH", nekofeng_y + layer->y_offset + i); 202 | printf("\033[%dG", nekofeng_x + layer->x_offset + j - 1); 203 | printf(" "); 204 | fflush(stdout); 205 | usleep(inr); 206 | } 207 | } 208 | free(buf); 209 | } 210 | #endif 211 | // NOLINTEND -------------------------------------------------------------------------------- /src/elf-magic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | #include "include/elf-magic.h" 32 | /* 33 | * This file provides functions to get ELF magic number and mask for cross_arch. 34 | * These info will be used for binfmt_misc. 35 | * Maybe it seems stupid, but it's okey. 36 | */ 37 | // Get ELF magic number and mask for cross_arch specified. 38 | struct RURI_ELF_MAGIC *ruri_get_magic(const char *_Nonnull cross_arch) 39 | { 40 | /* 41 | * I know that this seems to be a little bit stupid, 42 | * but we have no other ways to match the architecture to its ELF magic. 43 | * TODO: Support more architecture aliases. 44 | */ 45 | struct RURI_ELF_MAGIC *ret = (struct RURI_ELF_MAGIC *)malloc(sizeof(struct RURI_ELF_MAGIC)); 46 | // Avoid to simulate the same architecture as host. 47 | if (strcmp(cross_arch, RURI_HOST_ARCH) == 0) { 48 | ruri_error("Do not simulate the same architecture as host."); 49 | } 50 | if (strcmp(cross_arch, "aarch64") == 0 || strcmp(cross_arch, "arm64") == 0 || strcmp(cross_arch, "armv8") == 0) { 51 | if (strcmp(RURI_HOST_ARCH, "aarch64") == 0) { 52 | ruri_error("Do not simulate the same architecture as host."); 53 | } 54 | ret->magic = ruri_magicof(aarch64); 55 | ret->mask = ruri_maskof(aarch64); 56 | } else if (strcmp(cross_arch, "alpha") == 0) { 57 | ret->magic = ruri_magicof(alpha); 58 | ret->mask = ruri_maskof(alpha); 59 | } else if (strcmp(cross_arch, "arm") == 0 || strcmp(cross_arch, "armhf") == 0 || strcmp(cross_arch, "arm32") == 0 || strcmp(cross_arch, "armel") == 0 || strcmp(cross_arch, "armv7") == 0) { 60 | if (strcmp(RURI_HOST_ARCH, "arm") == 0) { 61 | ruri_error("Do not simulate the same architecture as host."); 62 | } 63 | ret->magic = ruri_magicof(arm); 64 | ret->mask = ruri_maskof(arm); 65 | } else if (strcmp(cross_arch, "armeb") == 0) { 66 | ret->magic = ruri_magicof(armeb); 67 | ret->mask = ruri_maskof(armeb); 68 | } else if (strcmp(cross_arch, "cris") == 0) { 69 | ret->magic = ruri_magicof(cris); 70 | ret->mask = ruri_maskof(cris); 71 | } else if (strcmp(cross_arch, "hexagon") == 0) { 72 | ret->magic = ruri_magicof(hexagon); 73 | ret->mask = ruri_maskof(hexagon); 74 | } else if (strcmp(cross_arch, "hppa") == 0) { 75 | ret->magic = ruri_magicof(hppa); 76 | ret->mask = ruri_maskof(hppa); 77 | } else if (strcmp(cross_arch, "i386") == 0 || strcmp(cross_arch, "x86") == 0) { 78 | if (strcmp(RURI_HOST_ARCH, "i386") == 0) { 79 | ruri_error("Do not simulate the same architecture as host."); 80 | } 81 | ret->magic = ruri_magicof(i386); 82 | ret->mask = ruri_maskof(i386); 83 | } else if (strcmp(cross_arch, "loongarch64") == 0 || strcmp(cross_arch, "loong64") == 0 || strcmp(cross_arch, "loongarch") == 0) { 84 | if (strcmp(RURI_HOST_ARCH, "loongarch64") == 0) { 85 | ruri_error("Do not simulate the same architecture as host."); 86 | } 87 | ret->magic = ruri_magicof(loongarch64); 88 | ret->mask = ruri_maskof(loongarch64); 89 | } else if (strcmp(cross_arch, "m68k") == 0) { 90 | ret->magic = ruri_magicof(m68k); 91 | ret->mask = ruri_maskof(m68k); 92 | } else if (strcmp(cross_arch, "microblaze") == 0) { 93 | ret->magic = ruri_magicof(microblaze); 94 | ret->mask = ruri_maskof(microblaze); 95 | } else if (strcmp(cross_arch, "mips") == 0) { 96 | ret->magic = ruri_magicof(mips); 97 | ret->mask = ruri_maskof(mips); 98 | } else if (strcmp(cross_arch, "mips64") == 0) { 99 | ret->magic = ruri_magicof(mips64); 100 | ret->mask = ruri_maskof(mips64); 101 | } else if (strcmp(cross_arch, "mips64el") == 0) { 102 | ret->magic = ruri_magicof(mips64el); 103 | ret->mask = ruri_maskof(mips64el); 104 | } else if (strcmp(cross_arch, "mipsel") == 0) { 105 | ret->magic = ruri_magicof(mipsel); 106 | ret->mask = ruri_maskof(mipsel); 107 | } else if (strcmp(cross_arch, "mipsn32") == 0) { 108 | ret->magic = ruri_magicof(mipsn32); 109 | ret->mask = ruri_maskof(mipsn32); 110 | } else if (strcmp(cross_arch, "mipsn32el") == 0) { 111 | ret->magic = ruri_magicof(mipsn32el); 112 | ret->mask = ruri_maskof(mipsn32el); 113 | } else if (strcmp(cross_arch, "ppc") == 0) { 114 | ret->magic = ruri_magicof(ppc); 115 | ret->mask = ruri_maskof(ppc); 116 | } else if (strcmp(cross_arch, "ppc64") == 0) { 117 | ret->magic = ruri_magicof(ppc64); 118 | ret->mask = ruri_maskof(ppc64); 119 | } else if (strcmp(cross_arch, "ppc64le") == 0) { 120 | ret->magic = ruri_magicof(ppc64le); 121 | ret->mask = ruri_maskof(ppc64le); 122 | } else if (strcmp(cross_arch, "riscv32") == 0) { 123 | ret->magic = ruri_magicof(riscv32); 124 | ret->mask = ruri_maskof(riscv32); 125 | } else if (strcmp(cross_arch, "riscv64") == 0) { 126 | ret->magic = ruri_magicof(riscv64); 127 | ret->mask = ruri_maskof(riscv64); 128 | } else if (strcmp(cross_arch, "s390x") == 0) { 129 | ret->magic = ruri_magicof(s390x); 130 | ret->mask = ruri_maskof(s390x); 131 | } else if (strcmp(cross_arch, "sh4") == 0) { 132 | ret->magic = ruri_magicof(sh4); 133 | ret->mask = ruri_maskof(sh4); 134 | } else if (strcmp(cross_arch, "sh4eb") == 0) { 135 | ret->magic = ruri_magicof(sh4eb); 136 | ret->mask = ruri_maskof(sh4eb); 137 | } else if (strcmp(cross_arch, "sparc") == 0) { 138 | ret->magic = ruri_magicof(sparc); 139 | ret->mask = ruri_maskof(sparc); 140 | } else if (strcmp(cross_arch, "sparc32plus") == 0) { 141 | ret->magic = ruri_magicof(sparc32plus); 142 | ret->mask = ruri_maskof(sparc32plus); 143 | } else if (strcmp(cross_arch, "sparc64") == 0) { 144 | ret->magic = ruri_magicof(sparc64); 145 | ret->mask = ruri_maskof(sparc64); 146 | } else if (strcmp(cross_arch, "x86_64") == 0 || strcmp(cross_arch, "amd64") == 0) { 147 | if (strcmp(RURI_HOST_ARCH, "x86_64") == 0) { 148 | ruri_error("Do not simulate the same architecture as host."); 149 | } 150 | ret->magic = ruri_magicof(x86_64); 151 | ret->mask = ruri_maskof(x86_64); 152 | } else if (strcmp(cross_arch, "xtensa") == 0) { 153 | ret->magic = ruri_magicof(xtensa); 154 | ret->mask = ruri_maskof(xtensa); 155 | } else if (strcmp(cross_arch, "xtensaeb") == 0) { 156 | ret->magic = ruri_magicof(xtensaeb); 157 | ret->mask = ruri_maskof(xtensaeb); 158 | } else { 159 | free(ret); 160 | return NULL; 161 | } 162 | ruri_log("Cross architecture: {cyan}%s\n", cross_arch); 163 | ruri_log("ELF magic: {cyan}%s\n", ret->magic); 164 | ruri_log("ELF mask: {cyan}%s\n", ret->mask); 165 | return ret; 166 | } 167 | -------------------------------------------------------------------------------- /src/include/cprintf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of cprintf, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | */ 29 | // NOLINTBEGIN 30 | #pragma once 31 | #ifdef __linux__ 32 | #define _GNU_SOURCE 33 | #endif 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #ifndef _Nullable 49 | #define _Nullable 50 | #endif 51 | #ifndef _Nonnull 52 | #define _Nonnull 53 | #endif 54 | #if __STDC_VERSION__ < 202000L 55 | #define bool _Bool 56 | #define true ((_Bool) + 1u) 57 | #define false ((_Bool) + 0u) 58 | #endif 59 | #define CPRINTF_MAJOR 2 60 | #define CPRINTF_MINOR 1 61 | bool cp_xterm_is_dark_mode(void); 62 | int cprintf__(const char *_Nonnull buf); 63 | int cfprintf__(FILE *_Nonnull stream, const char *_Nonnull buf); 64 | // Color support. 65 | struct CPRINTF_COLOR__ { 66 | char *base; 67 | char *red_fg; 68 | char *green_fg; 69 | char *yellow_fg; 70 | char *blue_fg; 71 | char *purple_fg; 72 | char *cyan_fg; 73 | char *white_fg; 74 | char *black_fg; 75 | char *red_bg; 76 | char *green_bg; 77 | char *yellow_bg; 78 | char *blue_bg; 79 | char *purple_bg; 80 | char *cyan_bg; 81 | char *white_bg; 82 | char *black_bg; 83 | }; 84 | extern struct CPRINTF_COLOR__ cprintf_color; 85 | #define cprintf_base_color cprintf_color.base 86 | 87 | // Do not print color if the stream is a FIFO. 88 | extern bool cprintf_print_color_only_tty; 89 | 90 | // Generic support. 91 | #define cprintf_strlen__(cpsl_f__) (cpsl_f__ == NULL ? 0 : strlen(cpsl_f__)) 92 | #define cprintf_avoid_null__(cpan_f__) (cpan_f__ == NULL ? "" : cpan_f__) 93 | #define cprintf_buf_len__(cpbl_f__, cpbl_d__) (cpbl_f__ != NULL ? (size_t)snprintf(NULL, 0, cpbl_f__, cpbl_d__) : 0) 94 | #define cprintf_get_fmt__(cpgf_d__, cpgf_f__) \ 95 | ({ \ 96 | char *cpgf_buf__ = malloc(cprintf_strlen__(cpgf_f__) + 16); \ 97 | sprintf(cpgf_buf__, _Generic((cpgf_d__), _Bool: (cpgf_d__ ? "true" : "false"), char: "%%%sc", signed char: "%%%sd", unsigned char: "%%%sd", short: "%%%shd", unsigned short: "%%%shu", int: "%%%sd", unsigned int: "%%%su", long: "%%%sld", unsigned long: "%%%slu", long long: "%%%slld", unsigned long long: "%%%sllu", float: "%%%sf", double: "%%%sf", long double: "%%%sLf", void *: "%%%sp", default: "{unknown}"), cprintf_avoid_null__(cpgf_f__)); \ 98 | cprintf_mark_buf__(cpgf_buf__); \ 99 | cpgf_buf__; \ 100 | }) 101 | 102 | #define cprintf_to_char__(cpdc_d__, cpdc_f__) \ 103 | ({ \ 104 | char *cptc_buf__ = malloc(cprintf_buf_len__(cpdc_f__, cpdc_d__) + 32); \ 105 | sprintf(cptc_buf__, cpdc_f__, cpdc_d__); \ 106 | cprintf_mark_buf__(cptc_buf__); \ 107 | cptc_buf__; \ 108 | }) 109 | 110 | #define F(cp_f_data__, cp_f_format__) cprintf_to_char__(cp_f_data__, cprintf_get_fmt__(cp_f_data__, cp_f_format__)) 111 | #define T(cp_t_data__) F(cp_t_data__, NULL) 112 | #define cprintf_len__(cpl_format__, ...) (snprintf(NULL, 0, cprintf_regen_format__(cpl_format__), ##__VA_ARGS__) + 8) 113 | #define csprintf(string, format, ...) \ 114 | ({ \ 115 | int csp_ret__ = 0; \ 116 | if (format == NULL) { \ 117 | csp_ret__ = sprintf(string, "%s", "(null)"); \ 118 | } else { \ 119 | char *csp_fmt__ = cprintf_regen_format__(format); \ 120 | csp_ret__ = sprintf(string, csp_fmt__, ##__VA_ARGS__); \ 121 | cprintf_free_buf__(); \ 122 | } \ 123 | csp_ret__; \ 124 | }) 125 | #define cprintf(format, ...) \ 126 | ({ \ 127 | int cp_ret__ = 0; \ 128 | char *cp_buf__ = malloc(cprintf_len__(format, ##__VA_ARGS__)); \ 129 | csprintf(cp_buf__, format, ##__VA_ARGS__); \ 130 | cp_ret__ = cprintf__(cp_buf__); \ 131 | free(cp_buf__); \ 132 | cp_ret__; \ 133 | }) 134 | #define cfprintf(stream, format, ...) \ 135 | ({ \ 136 | int cfp_ret__ = 0; \ 137 | char *cfp_buf__ = malloc(cprintf_len__(format, ##__VA_ARGS__)); \ 138 | csprintf(cfp_buf__, format, ##__VA_ARGS__); \ 139 | cfp_ret__ = cfprintf__(stream, cfp_buf__); \ 140 | free(cfp_buf__); \ 141 | cfp_ret__; \ 142 | }) 143 | #define scprintf(format, ...) \ 144 | { \ 145 | char *cp_buf__ = malloc((size_t)snprintf(NULL, 0, format, ##__VA_ARGS__) + 1); \ 146 | sprintf(cp_buf__, format, ##__VA_ARGS__); \ 147 | cprintf__(cp_buf__); \ 148 | free(cp_buf__); \ 149 | } 150 | #define scfprintf(stream, format, ...) \ 151 | { \ 152 | char *cfp_buf__ = malloc((size_t)snprintf(NULL, 0, format, ##__VA_ARGS__) + 1); \ 153 | sprintf(cfp_buf__, format, ##__VA_ARGS__); \ 154 | cfprintf__(stream, cfp_buf__); \ 155 | free(cfp_buf__); \ 156 | } 157 | // For generic support. 158 | char *cprintf_regen_format__(const char *f); 159 | void cprintf_free_buf__(void); 160 | void cprintf_mark_buf__(char *b); 161 | // NOLINTEND 162 | -------------------------------------------------------------------------------- /src/include/elf-magic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header contains no copyrightable information. 3 | */ 4 | // Do not format this. 5 | // clang-format off 6 | /* ELF magic header and mask for binfmt_misc & QEMU. */ 7 | #define aarch64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xb7\\x00" 8 | #define alpha_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x26\\x90" 9 | #define arm_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00" 10 | #define armeb_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28" 11 | #define cris_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x4c\\x00" 12 | #define hexagon_magic "\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xa4\\x00" 13 | #define hppa_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x0f" 14 | #define i386_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x03\\x00" 15 | #define loongarch64_magic "\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x02\\x01" 16 | #define m68k_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04" 17 | #define microblaze_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\xba\\xab" 18 | #define mips_magic "\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" 19 | #define mips64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08" 20 | #define mips64el_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08\\x00" 21 | #define mipsel_magic "\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" 22 | #define mipsn32_magic "\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20" 23 | #define mipsn32el_magic "\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x08\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00" 24 | #define ppc_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14" 25 | #define ppc64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x15" 26 | #define ppc64le_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x15\\x00" 27 | #define riscv32_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xf3\\x00" 28 | #define riscv64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xf3\\x00" 29 | #define s390x_magic "\\x7f\\x45\\x4c\\x46\\x02\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x16" 30 | #define sh4_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x2a\\x00" 31 | #define sh4eb_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x2a" 32 | #define sparc_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x02" 33 | #define sparc32plus_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x12" 34 | #define sparc64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x2b" 35 | #define x86_64_magic "\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x3e\\x00" 36 | #define xtensa_magic "\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x5e\\x00" 37 | #define xtensaeb_magic "\\x7f\\x45\\x4c\\x46\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x5e" 38 | #define aarch64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 39 | #define alpha_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 40 | #define arm_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 41 | #define armeb_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 42 | #define cris_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 43 | #define hexagon_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 44 | #define hppa_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 45 | #define i386_mask "\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 46 | #define loongarch64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 47 | #define m68k_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 48 | #define microblaze_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 49 | #define mips_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20" 50 | #define mips64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 51 | #define mips64el_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 52 | #define mipsel_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00" 53 | #define mipsn32_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20" 54 | #define mipsn32el_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00" 55 | #define ppc_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 56 | #define ppc64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 57 | #define ppc64le_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\x00" 58 | #define riscv32_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 59 | #define riscv64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 60 | #define s390x_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 61 | #define sh4_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 62 | #define sh4eb_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 63 | #define sparc_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 64 | #define sparc32plus_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 65 | #define sparc64_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 66 | #define x86_64_mask "\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\xfc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 67 | #define xtensa_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff" 68 | #define xtensaeb_mask "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff" 69 | -------------------------------------------------------------------------------- /src/include/hostarch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header contains no copyrightable information. 3 | */ 4 | #if defined(__aarch64__) 5 | #define RURI_HOST_ARCH "aarch64" 6 | #endif 7 | #if defined(__alpha__) 8 | #define RURI_HOST_ARCH "alpha" 9 | #endif 10 | #if defined(__arm__) 11 | #define RURI_HOST_ARCH "arm" 12 | #endif 13 | #if defined(__armeb__) 14 | #define RURI_HOST_ARCH "armeb" 15 | #endif 16 | #if defined(__cris__) 17 | #define RURI_HOST_ARCH "cris" 18 | #endif 19 | #if defined(__hexagon__) 20 | #define RURI_HOST_ARCH "hexagon" 21 | #endif 22 | #if defined(__hppa__) 23 | #define RURI_HOST_ARCH "hppa" 24 | #endif 25 | #if defined(__i386__) 26 | #define RURI_HOST_ARCH "i386" 27 | #endif 28 | #if defined(__loongarch64__) 29 | #define RURI_HOST_ARCH "loongarch64" 30 | #endif 31 | #if defined(__m68k__) 32 | #define RURI_HOST_ARCH "m68k" 33 | #endif 34 | #if defined(__microblaze__) 35 | #define RURI_HOST_ARCH "microblaze" 36 | #endif 37 | #if defined(__mips__) 38 | #define RURI_HOST_ARCH "mips" 39 | #endif 40 | #if defined(__mips64__) 41 | #define RURI_HOST_ARCH "mips64" 42 | #endif 43 | #if defined(__mips64el__) 44 | #define RURI_HOST_ARCH "mips64el" 45 | #endif 46 | #if defined(__mipsel__) 47 | #define RURI_HOST_ARCH "mipsel" 48 | #endif 49 | #if defined(__mipsn32__) 50 | #define RURI_HOST_ARCH "mipsn32" 51 | #endif 52 | #if defined(__mipsn32el__) 53 | #define RURI_HOST_ARCH "mipsn32el" 54 | #endif 55 | #if defined(__ppc__) 56 | #define RURI_HOST_ARCH "ppc" 57 | #endif 58 | #if defined(__ppc64__) 59 | #define RURI_HOST_ARCH "ppc64" 60 | #endif 61 | #if defined(__ppc64le__) 62 | #define RURI_HOST_ARCH "ppc64le" 63 | #endif 64 | #if defined(__riscv32__) 65 | #define RURI_HOST_ARCH "riscv32" 66 | #endif 67 | #if defined(__riscv64__) 68 | #define RURI_HOST_ARCH "riscv64" 69 | #endif 70 | #if defined(__s390x__) 71 | #define RURI_HOST_ARCH "s390x" 72 | #endif 73 | #if defined(__sh4__) 74 | #define RURI_HOST_ARCH "sh4" 75 | #endif 76 | #if defined(__sh4eb__) 77 | #define RURI_HOST_ARCH "sh4eb" 78 | #endif 79 | #if defined(__sparc__) 80 | #define RURI_HOST_ARCH "sparc" 81 | #endif 82 | #if defined(__sparc32plus__) 83 | #define RURI_HOST_ARCH "sparc32plus" 84 | #endif 85 | #if defined(__sparc64__) 86 | #define RURI_HOST_ARCH "sparc64" 87 | #endif 88 | #if defined(__x86_64__) 89 | #define RURI_HOST_ARCH "x86_64" 90 | #endif 91 | #if defined(__xtensa__) 92 | #define RURI_HOST_ARCH "xtensa" 93 | #endif 94 | #if defined(__xtensaeb__) 95 | #define RURI_HOST_ARCH "xtensaeb" 96 | #endif 97 | #if !defined(RURI_HOST_ARCH) 98 | #define RURI_HOST_ARCH "unknown" 99 | #endif -------------------------------------------------------------------------------- /src/include/k2v.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of libk2v, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #pragma once 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | // Bool!!! 38 | #if __STDC_VERSION__ < 202000L 39 | #ifndef bool 40 | #define bool _Bool 41 | #define true ((_Bool) + 1u) 42 | #define false ((_Bool) + 0u) 43 | #endif 44 | #endif 45 | #ifndef _Nullable 46 | #define _Nullable 47 | #endif 48 | #ifndef _Nonnull 49 | #define _Nonnull 50 | #endif 51 | // Version info. 52 | #define LIBK2V_MAJOR 2 53 | #define LIBK2V_MINOR 2 54 | // Warning. 55 | extern bool k2v_stop_at_warning; 56 | extern bool k2v_show_warning; 57 | // Functions. 58 | char *key_get_char(const char *_Nonnull key, const char *_Nonnull buf); 59 | int key_get_int(const char *_Nonnull key, const char *_Nonnull buf); 60 | float key_get_float(const char *_Nonnull key, const char *_Nonnull buf); 61 | bool key_get_bool(const char *_Nonnull key, const char *_Nonnull buf); 62 | int key_get_int_array(const char *_Nonnull key, const char *_Nonnull buf, int *_Nonnull array, int limit); 63 | int key_get_char_array(const char *_Nonnull key, const char *_Nonnull buf, char *_Nonnull array[], int limit); 64 | int key_get_float_array(const char *_Nonnull key, const char *_Nonnull buf, float *_Nonnull array, int limit); 65 | bool have_key(const char *_Nonnull key, const char *_Nonnull buf); 66 | char *k2v_open_file(const char *_Nonnull path, size_t bufsize); 67 | void k2v_to_shell(const char *_Nonnull buf); 68 | char *char_to_k2v(const char *_Nonnull key, const char *val); 69 | char *int_to_k2v(const char *_Nonnull key, int val); 70 | char *bool_to_k2v(const char *_Nonnull key, bool val); 71 | char *float_to_k2v(const char *_Nonnull key, float val); 72 | char *char_array_to_k2v(const char *_Nonnull key, char *const *_Nonnull val, int len); 73 | char *int_array_to_k2v(const char *_Nonnull key, int *_Nonnull val, int len); 74 | char *float_array_to_k2v(const char *_Nonnull key, float *_Nonnull val, int len); 75 | size_t k2v_get_filesize(const char *_Nonnull path); 76 | #define k2v_get_key(type, ...) key_get_##type(__VA_ARGS__) 77 | #define k2v_add_config(type, __k2v_buf, ...) \ 78 | ({ \ 79 | char *__k2v_tmp = type##_to_k2v(__VA_ARGS__); \ 80 | size_t __k2v_size = 4; \ 81 | if (__k2v_buf != NULL) { \ 82 | __k2v_size += strlen(__k2v_buf); \ 83 | } \ 84 | __k2v_size += strlen(__k2v_tmp) + 4; \ 85 | char *__k2v_ret = malloc(__k2v_size); \ 86 | if (__k2v_buf != NULL) { \ 87 | sprintf(__k2v_ret, "%s%s", __k2v_buf, __k2v_tmp); \ 88 | } else { \ 89 | sprintf(__k2v_ret, "%s", __k2v_tmp); \ 90 | } \ 91 | free(__k2v_buf); \ 92 | free(__k2v_tmp); \ 93 | __k2v_ret; \ 94 | }) 95 | char *k2v_add_comment(char *_Nullable buf, char *_Nonnull comment); 96 | char *k2v_add_newline(char *_Nullable buf); 97 | long long key_get_long(const char *_Nonnull key, const char *_Nonnull buf); 98 | char *long_to_k2v(const char *_Nonnull key, long val); -------------------------------------------------------------------------------- /src/include/version.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | * 30 | */ 31 | // Version info. 32 | #define RURI_VERSION "3.9" 33 | #define RURI_VERSION_MAJOR 3 34 | #define RURI_VERSION_MINOR 9 35 | #define RURI_VERSION_PATCH 3 -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * main() calls ruri() directly, 33 | * so that we can make ruri built-in into other programs easily. 34 | */ 35 | int main(int argc, char **argv) 36 | { 37 | /* 38 | * Nothing here, just call ruri(). 39 | * So that we can make ruri built-in into other programs easily. 40 | */ 41 | for (int i = 0; i < argc; i++) { 42 | ruri_log("{base}argv[%d]: {cyan}%s\n", i, argv[i]); 43 | } 44 | return ruri(argc, argv); 45 | } -------------------------------------------------------------------------------- /src/ps.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * This file provides functions to show or kill all processes in the container. 33 | * Note: 34 | * For unshare container without pid ns, 35 | * we can not recognize the pids in container. 36 | * And for that with pid ns, just kill pid 1 of the ns, 37 | * and all processes will be destroyed. 38 | */ 39 | static char *getpid_name(pid_t pid) 40 | { 41 | /* 42 | * Get the name of the process by pid. 43 | * Warning: free() the return value after using it. 44 | */ 45 | char path[PATH_MAX]; 46 | sprintf(path, "%s%d%s", "/proc/", pid, "/stat"); 47 | char buf[8192]; 48 | char name_buf[PATH_MAX]; 49 | int fd = open(path, O_RDONLY | O_CLOEXEC); 50 | read(fd, buf, sizeof(buf)); 51 | if (fd < 0) { 52 | return strdup(" "); 53 | } 54 | int j = 0; 55 | for (unsigned long i = 0; i < sizeof(buf); i++) { 56 | if (j == 1) { 57 | for (unsigned long k = 0; buf[k + i + 1] != ')'; k++) { 58 | name_buf[k] = buf[k + i + 1]; 59 | name_buf[k + 1] = '\0'; 60 | } 61 | break; 62 | } 63 | if (buf[i] == ' ') { 64 | j++; 65 | } 66 | } 67 | char *name = strdup(name_buf); 68 | return name; 69 | } 70 | static char *getpid_stat(pid_t pid) 71 | { 72 | /* 73 | * Get the status of the process by pid. 74 | * Warning: free() the return value after using it. 75 | */ 76 | char path[PATH_MAX]; 77 | sprintf(path, "%s%d%s", "/proc/", pid, "/stat"); 78 | char buf[8192]; 79 | char stat_buf[PATH_MAX]; 80 | int fd = open(path, O_RDONLY | O_CLOEXEC); 81 | if (fd < 0) { 82 | return strdup(" "); 83 | } 84 | read(fd, buf, sizeof(buf)); 85 | int j = 0; 86 | for (unsigned long i = 0; i < sizeof(buf); i++) { 87 | if (j == 2) { 88 | for (unsigned long k = 0; buf[k + i] != ' '; k++) { 89 | stat_buf[k] = buf[k + i]; 90 | stat_buf[k + 1] = '\0'; 91 | } 92 | break; 93 | } 94 | if (buf[i] == ' ') { 95 | j++; 96 | } 97 | } 98 | char *pid_status = strdup(stat_buf); 99 | return pid_status; 100 | } 101 | static void test_and_print_pid(pid_t pid, char *_Nonnull container_dir, bool in_pid_ns) 102 | { 103 | /* 104 | * If /proc/pid/root is container_dir, print the pid. 105 | */ 106 | char path[PATH_MAX]; 107 | sprintf(path, "%s%d%s", "/proc/", pid, "/root"); 108 | char buf[PATH_MAX]; 109 | realpath(path, buf); 110 | ruri_log("{base}Pid: {cyan}%d\n", pid); 111 | ruri_log("{base}Root: {cyan}%s\n", buf); 112 | if (strcmp(buf, container_dir) == 0) { 113 | char *name = getpid_name(pid); 114 | char *pid_status = getpid_stat(pid); 115 | if (name != NULL && pid_status != NULL) { 116 | if (in_pid_ns) { 117 | printf("--> %d %s %s\n", pid, name, pid_status); 118 | } else { 119 | printf("%d %s %s\n", pid, name, pid_status); 120 | } 121 | } 122 | free(name); 123 | free(pid_status); 124 | } 125 | } 126 | static void container_ps__(char *_Nonnull container_dir, bool in_pid_ns) 127 | { 128 | /* 129 | * Show the processes in the container. 130 | * This is the core function of ruri_container_ps(). 131 | */ 132 | DIR *proc_dir = opendir("/proc"); 133 | struct dirent *file = NULL; 134 | int len = 0; 135 | while ((file = readdir(proc_dir)) != NULL) { 136 | if (file->d_type == DT_DIR) { 137 | len++; 138 | } 139 | } 140 | seekdir(proc_dir, 0); 141 | int pids[len + 11]; 142 | // For passing clang-tidy. 143 | memset(pids, 0, sizeof(pids)); 144 | int i = 0; 145 | while ((file = readdir(proc_dir)) != NULL) { 146 | if (file->d_type == DT_DIR) { 147 | if (atoi(file->d_name) > 0) { 148 | pids[i] = atoi(file->d_name); 149 | pids[i + 1] = RURI_INIT_VALUE; 150 | i++; 151 | } 152 | } 153 | } 154 | for (int j = 0; j < len; j++) { 155 | if (pids[j] != RURI_INIT_VALUE) { 156 | ruri_log("{base}Checking pid: {cyan}%d\n", pids[j]); 157 | test_and_print_pid(pids[j], container_dir, in_pid_ns); 158 | } else { 159 | break; 160 | } 161 | } 162 | closedir(proc_dir); 163 | } 164 | static int join_ns(pid_t ns_pid) 165 | { 166 | /* 167 | * Try to join the pid and mount namespace of the container. 168 | * If failed, return -1. 169 | */ 170 | if (geteuid() != 0) { 171 | ruri_error("{red}Error: Please run `ruri -P` with sudo.\n"); 172 | } 173 | char path[PATH_MAX]; 174 | sprintf(path, "%s%d%s", "/proc/", ns_pid, "/ns/pid"); 175 | int fd = open(path, O_RDONLY | O_CLOEXEC); 176 | if (fd < 0) { 177 | return -1; 178 | } 179 | setns(fd, 0); 180 | close(fd); 181 | ruri_log("{base}Joined pid namespace\n"); 182 | sprintf(path, "%s%d%s", "/proc/", ns_pid, "/ns/mnt"); 183 | fd = open(path, O_RDONLY | O_CLOEXEC); 184 | if (fd < 0) { 185 | return -1; 186 | } 187 | setns(fd, 0); 188 | close(fd); 189 | chdir("/"); 190 | ruri_log("{base}Joined mount namespace\n"); 191 | return 0; 192 | } 193 | void ruri_container_ps(char *_Nonnull container_dir) 194 | { 195 | /* 196 | * Show the processes in the container. 197 | * We check the root of each process, if it is the container directory, print the pid. 198 | * if this is an unshare container, join the pid and mount namespace, so root is "/" now. 199 | * if pid namespace is not supported, fallback to the behavior with common container. 200 | */ 201 | ruri_log("{base}Container directory: {cyan}%s\n", container_dir); 202 | if (geteuid() != 0) { 203 | ruri_warning("{yellow}Warning: Please run `ruri -P` with sudo.\n"); 204 | } 205 | struct RURI_CONTAINER *container = ruri_read_info(NULL, container_dir); 206 | bool in_pid_ns = false; 207 | if (container->ns_pid > 0) { 208 | // We need to get the info of ns_pid before joining the namespace. 209 | char *name = getpid_name(container->ns_pid); 210 | char *pid_status = getpid_stat(container->ns_pid); 211 | if (join_ns(container->ns_pid) == 0) { 212 | printf("%d %s %s\n", container->ns_pid, name, pid_status); 213 | in_pid_ns = true; 214 | // If we successfully joined the namespace, 215 | // The root of the process is now "/". 216 | container_dir = "/"; 217 | } 218 | free(name); 219 | free(pid_status); 220 | } 221 | container_ps__(container_dir, in_pid_ns); 222 | free(container); 223 | exit(EXIT_SUCCESS); 224 | } 225 | static bool is_container_process(pid_t pid, const char *_Nonnull container_dir) 226 | { 227 | /* 228 | * Check if the process is in the container. 229 | */ 230 | char path[PATH_MAX]; 231 | sprintf(path, "%s%d%s", "/proc/", pid, "/root"); 232 | char buf[PATH_MAX]; 233 | buf[0] = '\0'; 234 | realpath(path, buf); 235 | if (strcmp(buf, "/") == 0) { 236 | return false; 237 | } 238 | if (strcmp(buf, container_dir) == 0) { 239 | return true; 240 | } 241 | return false; 242 | } 243 | void ruri_kill_container(const char *_Nonnull container_dir) 244 | { 245 | /* 246 | * 247 | * Check all the processes in /proc, 248 | * If the process is in the container, kill it. 249 | * We check for /proc/pid/root to determine if the process is in the container. 250 | * This function is called by ruri_umount_container(). 251 | */ 252 | DIR *proc_dir = opendir("/proc"); 253 | struct dirent *file = NULL; 254 | int len = 0; 255 | while ((file = readdir(proc_dir)) != NULL) { 256 | if (file->d_type == DT_DIR) { 257 | len++; 258 | } 259 | } 260 | seekdir(proc_dir, 0); 261 | int pids[len + 11]; 262 | // For passing clang-tidy. 263 | memset(pids, 0, sizeof(pids)); 264 | int i = 0; 265 | while ((file = readdir(proc_dir)) != NULL) { 266 | if (file->d_type == DT_DIR) { 267 | if (atoi(file->d_name) > 0) { 268 | pids[i] = atoi(file->d_name); 269 | pids[i + 1] = RURI_INIT_VALUE; 270 | i++; 271 | } 272 | } 273 | } 274 | for (int j = 0; j < len; j++) { 275 | if (pids[j] != RURI_INIT_VALUE) { 276 | ruri_log("{base}Checking pid: {cyan}%d\n", pids[j]); 277 | if (is_container_process(pids[j], container_dir)) { 278 | ruri_log("{base}Killing pid: {cyan}%d\n", pids[j]); 279 | kill(pids[j], SIGKILL); 280 | } 281 | } else { 282 | break; 283 | } 284 | } 285 | closedir(proc_dir); 286 | } -------------------------------------------------------------------------------- /src/rurifetch.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * A neofetch-like program for ruri. 33 | * Nothing useful, just for fun. 34 | */ 35 | #ifndef RURI_CORE_ONLY 36 | static void ruri_fetch__(char **logo, char **info) 37 | { 38 | int j = 0; 39 | for (int i = 0; logo[i] != NULL; i++) { 40 | if (info[j] != NULL) { 41 | cprintf("%s%s\n", logo[i], info[j]); 42 | j++; 43 | } else { 44 | cprintf("%s\n", logo[i]); 45 | } 46 | } 47 | } 48 | void ruri_fetch(void) 49 | { 50 | char *ruri_logo[24] = { NULL }; 51 | // clang-format off 52 | ruri_logo[0] = "{base} _-###-_ "; 53 | ruri_logo[1] = "{base} _## *** ##_ "; 54 | ruri_logo[2] = "{base} _## * * * ##_ "; 55 | ruri_logo[3] = "{base} ### * * * ### "; 56 | ruri_logo[4] = "{base} ## * * * ## "; 57 | ruri_logo[5] = "{base} #**** _ * * * _ ****# "; 58 | ruri_logo[6] = "{base} # * *_ **__ * * * __**_ * * # "; 59 | ruri_logo[7] = "{base} # * _**_ *_ * * * _* _**_ * # "; 60 | ruri_logo[8] = "{base} # **_* * *** * *_** # "; 61 | ruri_logo[9] = "{base} # ****+++**** # "; 62 | ruri_logo[10] = "{base} # **`* * *** * *`** # "; 63 | ruri_logo[11] = "{base} # * `**` *` * * * `* `**` * # "; 64 | ruri_logo[12] = "{base} # * *` **`` * * * ``**` * * # "; 65 | ruri_logo[13] = "{base} #**** ` * * * ` ****# "; 66 | ruri_logo[14] = "{base} ## * * * ## "; 67 | ruri_logo[15] = "{base} ### * * * ### "; 68 | ruri_logo[16] = "{base} `## * * * ##` "; 69 | ruri_logo[17] = "{base} `## *** ##` "; 70 | ruri_logo[18] = "{base} ```-###-``` "; 71 | ruri_logo[19] = NULL; 72 | // clang-format on 73 | char *ruri_info[24] = { NULL }; 74 | ruri_info[0] = "{91;207;250}Moe-hacker{white}@{91;207;250}Github"; 75 | ruri_info[1] = "{white}-----------------"; 76 | ruri_info[2] = "{91;207;250}Project{white}: ruri"; 77 | ruri_info[3] = "{91;207;250}License{white}: MIT"; 78 | char version_info[128] = { '\0' }; 79 | sprintf(version_info, "{91;207;250}Version{white}: %s", RURI_VERSION); 80 | ruri_info[4] = version_info; 81 | #if !defined(RURI_COMMIT_ID) 82 | #define RURI_COMMIT_ID "unknown" 83 | #endif 84 | char commit_id[128] = { '\0' }; 85 | sprintf(commit_id, "{91;207;250}Commit{white}: %s", RURI_COMMIT_ID); 86 | ruri_info[5] = commit_id; 87 | char host_arch[128] = { '\0' }; 88 | sprintf(host_arch, "{91;207;250}Architecture{white}: %s", RURI_HOST_ARCH); 89 | ruri_info[6] = host_arch; 90 | struct stat st; 91 | char binary_size[128] = { '\0' }; 92 | if (stat("/proc/self/exe", &st) == 0) { 93 | sprintf(binary_size, _Generic((off_t)0, long: "{91;207;250}Binary size{white}: %ldK", long long: "{91;207;250}Binary size{white}: %lldK", default: "{91;207;250}Binary size{white}: %ldK"), (st.st_size / 1024)); 94 | } else { 95 | sprintf(binary_size, "{91;207;250}Binary size{white}: unknown"); 96 | } 97 | ruri_info[7] = binary_size; 98 | char compiler_info[128] = { '\0' }; 99 | sprintf(compiler_info, "{91;207;250}Compiler{white}: %s", __VERSION__); 100 | ruri_info[8] = compiler_info; 101 | char build_date[128] = { '\0' }; 102 | sprintf(build_date, "{91;207;250}Build date{white}: %s", __DATE__); 103 | ruri_info[9] = build_date; 104 | char cprintf_version[128] = { '\0' }; 105 | sprintf(cprintf_version, "{91;207;250}cprintf{white}: %d.%d", CPRINTF_MAJOR, CPRINTF_MINOR); 106 | ruri_info[10] = cprintf_version; 107 | char libk2v_version[128] = { '\0' }; 108 | sprintf(libk2v_version, "{91;207;250}libk2v{white}: %d.%d", LIBK2V_MAJOR, LIBK2V_MINOR); 109 | ruri_info[11] = libk2v_version; 110 | #if !defined(LIBCAP_MAJOR) || !defined(LIBCAP_MINOR) 111 | ruri_info[12] = "{91;207;250}libcap{white}: unknown"; 112 | #else 113 | char libcap_version[128] = { '\0' }; 114 | sprintf(libcap_version, "{91;207;250}libcap{white}: %d.%d", LIBCAP_MAJOR, LIBCAP_MINOR); 115 | ruri_info[12] = libcap_version; 116 | #endif 117 | #if !defined(SCMP_VER_MAJOR) || !defined(SCMP_VER_MINOR) || !defined(SCMP_VER_MICRO) 118 | ruri_info[13] = "{91;207;250}libseccomp{white}: unknown"; 119 | #else 120 | char libseccomp_version[128] = { '\0' }; 121 | sprintf(libseccomp_version, "{91;207;250}libseccomp{white}: %d.%d.%d", SCMP_VER_MAJOR, SCMP_VER_MINOR, SCMP_VER_MICRO); 122 | ruri_info[13] = libseccomp_version; 123 | #endif 124 | ruri_info[14] = " "; 125 | ruri_info[15] = "[black] [red] [green] [yellow] [blue] [purple] [cyan] [white] [clear]"; 126 | ruri_info[16] = "\033[48;5;243m \033[48;5;196m \033[48;5;46m \033[48;5;226m \033[48;5;33m \033[48;5;201m \033[48;5;51m \033[48;5;15m \033[0m"; 127 | ruri_info[17] = NULL; 128 | ruri_fetch__(ruri_logo, ruri_info); 129 | } 130 | #else 131 | void ruri_fetch(void) 132 | { 133 | cprintf("{red}ruri was build with core-only mode QwQ.\n"); 134 | } 135 | #endif -------------------------------------------------------------------------------- /src/signal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * This file is used to catch segfault, 33 | * So that we can show some extra info when segfault. 34 | * I hope my program will never panic() QwQ. 35 | */ 36 | // Show some extra info when segfault. 37 | static void panic(int sig) 38 | { 39 | /* 40 | * This is very useful when bug reporting, 41 | * because we will get the cmdline that caused the error. 42 | */ 43 | signal(sig, SIG_DFL); 44 | int clifd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC); 45 | char buf[1024]; 46 | ssize_t bufsize = read(clifd, buf, sizeof(buf)); 47 | close(clifd); 48 | cfprintf(stderr, "{base}"); 49 | cfprintf(stderr, "{base}%s\n", " .^. .^."); 50 | cfprintf(stderr, "{base}%s\n", " /⋀\\_ノ_/⋀\\"); 51 | cfprintf(stderr, "{base}%s\n", " /ノソノ\\ノソ丶)|"); 52 | cfprintf(stderr, "{base}%s\n", " ルリリ > x )リ"); 53 | cfprintf(stderr, "{base}%s\n", "ノノ㇏ ^ ノ|ノ"); 54 | cfprintf(stderr, "{base}%s\n", " ⠁⠁"); 55 | cfprintf(stderr, "{base}%s\n", "RURI ERROR MESSAGE"); 56 | cfprintf(stderr, "{base}Seems that it's time to abort.\n"); 57 | cfprintf(stderr, "{base}SIG: %d\n", sig); 58 | cfprintf(stderr, "{base}UID: %u\n", getuid()); 59 | cfprintf(stderr, "{base}PID: %d\n", getpid()); 60 | cfprintf(stderr, "{base}CLI: "); 61 | for (ssize_t i = 0; i < bufsize - 1; i++) { 62 | if (buf[i] == '\0') { 63 | fputc(' ', stderr); 64 | } else { 65 | fputc(buf[i], stderr); 66 | } 67 | } 68 | // I'm afraid to have bugs written by myself, 69 | // but I'm more afraid that no one will report them. 70 | cfprintf(stderr, "{base}\nThis message might caused by an internal error.\n"); 71 | cfprintf(stderr, "{base}If you think something is wrong, please report at:\n"); 72 | cfprintf(stderr, "\033[4m{base}%s{clear}\n\n", "https://github.com/Moe-hacker/ruri/issues"); 73 | exit(114); 74 | } 75 | // Catch coredump signal. 76 | void ruri_register_signal(void) 77 | { 78 | /* 79 | * Only SIGSEGV means segmentation fault, 80 | * but we catch all signals that might cause coredump. 81 | */ 82 | signal(SIGABRT, panic); 83 | signal(SIGBUS, panic); 84 | signal(SIGFPE, panic); 85 | signal(SIGILL, panic); 86 | signal(SIGQUIT, panic); 87 | signal(SIGSEGV, panic); 88 | signal(SIGSYS, panic); 89 | signal(SIGTRAP, panic); 90 | signal(SIGXCPU, panic); 91 | signal(SIGXFSZ, panic); 92 | } 93 | -------------------------------------------------------------------------------- /src/umount.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * 4 | * This file is part of ruri, with ABSOLUTELY NO WARRANTY. 5 | * 6 | * MIT License 7 | * 8 | * Copyright (c) 2022-2024 Moe-hacker 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | * 29 | */ 30 | #include "include/ruri.h" 31 | /* 32 | * This file provides function to umount the container. 33 | * All pids detected in the container will be killed at the same time. 34 | * TODO: 35 | * Maybe we should use info in /proc/mounts first? 36 | */ 37 | static char *proc_mounts(void) 38 | { 39 | /* 40 | * Read /proc/mounts 41 | * Warning: free() after use. 42 | */ 43 | // As procfs does not support stat(), 44 | // that means we can not know the size of /proc/mounts, 45 | // so we have to use a buffer to read it. 46 | int fd = open("/proc/mounts", O_RDONLY | O_CLOEXEC); 47 | if (fd < 0) { 48 | return NULL; 49 | } 50 | char buf[1024 + 1]; 51 | size_t bufsize = 1024; 52 | ssize_t bytes_read = 0; 53 | char *ret = malloc(bufsize); 54 | ret[0] = '\0'; 55 | while ((bytes_read = read(fd, buf, 1024)) > 0) { 56 | bufsize += 1024; 57 | ret = realloc(ret, bufsize); 58 | buf[bytes_read] = '\0'; 59 | strcat(ret, buf); 60 | } 61 | close(fd); 62 | return ret; 63 | } 64 | static char *goto_next_line(const char *_Nonnull buf) 65 | { 66 | /* 67 | * Goto next line, without any check. 68 | * We assume all input is legal. 69 | */ 70 | if (strchr(buf, '\n') == NULL) { 71 | return NULL; 72 | } 73 | return strchr(buf, '\n') + 1; 74 | } 75 | static void umount_subdir(const char *_Nonnull dir) 76 | { 77 | /* 78 | * Umount subdir, use info in /proc/mounts 79 | * This is another implementation of umount_container, 80 | * we use it as a double-check. 81 | */ 82 | 83 | /* 84 | * /proc/mounts format: 85 | * device mount_point filesystem_type options dump fsck_order 86 | * We just use strstr to find `dir` in /proc/mounts, 87 | * and umount it. 88 | * This is okey in most scenarios. 89 | */ 90 | char *mount_info = proc_mounts(); 91 | if (mount_info == NULL) { 92 | return; 93 | } 94 | // A simple way to check if container is umounted. 95 | if (strstr(mount_info, dir) != NULL) { 96 | ruri_log("{base}There's still umounted dirs, using info in /proc/mounts to umount them\n"); 97 | } else { 98 | // Make ASAN happy. 99 | free(mount_info); 100 | return; 101 | } 102 | char *umount_point = NULL; 103 | char *p = mount_info; 104 | while ((p = strstr(p, dir)) != NULL) { 105 | if (p == NULL) { 106 | break; 107 | } 108 | // To avoid that we have dir=/foo but strstr find /foobar. 109 | if (p[strlen(dir)] != '/' && p[strlen(dir) - 1] != '/' && p[strlen(dir)] != ' ') { 110 | p = goto_next_line(p); 111 | continue; 112 | } 113 | umount_point = strdup(p); 114 | *strchr(umount_point, ' ') = '\0'; 115 | ruri_log("{base}Umounting %s{green}\n", umount_point); 116 | umount2(umount_point, MNT_DETACH | MNT_FORCE); 117 | free(umount_point); 118 | p = goto_next_line(p); 119 | } 120 | // Make ASAN happy. 121 | free(mount_info); 122 | } 123 | // Umount container. 124 | void ruri_umount_container(const char *_Nonnull container_dir) 125 | { 126 | /* 127 | * Read /.rurienv file and umount all mountpoints, 128 | * including extra_mountpoint and extra_ro_mountpoint, 129 | * and umount system runtime directories. 130 | */ 131 | if (container_dir == NULL) { 132 | ruri_error("{red}Error: container directory does not exist QwQ\n"); 133 | } 134 | // Do not use '/' for container_dir. 135 | if (strcmp(container_dir, "/") == 0) { 136 | ruri_error("{red}Error: `/` is not allowed to use as a container directory QwQ\n"); 137 | } 138 | // Check if container_dir exist. 139 | char *test = realpath(container_dir, NULL); 140 | if (test == NULL) { 141 | ruri_error("{red}Error: container directory does not exist QwQ\n"); 142 | } 143 | free(test); 144 | struct RURI_CONTAINER *container = ruri_read_info(NULL, container_dir); 145 | ruri_log("{base}Umounting container...\n"); 146 | char infofile[PATH_MAX] = { '\0' }; 147 | sprintf(infofile, "%s/.rurienv", container_dir); 148 | // Umount .rurienv file. 149 | umount2(infofile, MNT_DETACH | MNT_FORCE); 150 | int fd = open(infofile, O_RDONLY | O_CLOEXEC); 151 | // Unset immutable flag on .rurienv. 152 | int attr = 0; 153 | if (fd >= 0) { 154 | ioctl(fd, FS_IOC_GETFLAGS, &attr); 155 | attr &= ~FS_IMMUTABLE_FL; 156 | ioctl(fd, FS_IOC_SETFLAGS, &attr); 157 | remove(infofile); 158 | close(fd); 159 | } else { 160 | ruri_warning("{yellow}Warning: .rurienv does not exist\n"); 161 | } 162 | // Get path to umount. 163 | char sys_dir[PATH_MAX]; 164 | char proc_dir[PATH_MAX]; 165 | char dev_dir[PATH_MAX]; 166 | char to_umountpoint[PATH_MAX]; 167 | strcpy(sys_dir, container_dir); 168 | strcpy(proc_dir, container_dir); 169 | strcpy(dev_dir, container_dir); 170 | strcat(sys_dir, "/sys"); 171 | strcat(proc_dir, "/proc"); 172 | strcat(dev_dir, "/dev"); 173 | // Umount other mountpoints. 174 | if (container != NULL) { 175 | // Umount extra_mountpoint. 176 | for (int i = 1; true; i += 2) { 177 | if (container->extra_mountpoint[i] != NULL) { 178 | strcpy(to_umountpoint, container_dir); 179 | strcat(to_umountpoint, container->extra_mountpoint[i]); 180 | ruri_log("{base}Umounting %s\n", to_umountpoint); 181 | for (int j = 0; j < 10; j++) { 182 | umount2(to_umountpoint, MNT_DETACH); 183 | umount(to_umountpoint); 184 | usleep(20000); 185 | } 186 | // Remove the empty file we created for mounting files into container. 187 | remove(to_umountpoint); 188 | // Make ASAN happy. 189 | free(container->extra_mountpoint[i]); 190 | free(container->extra_mountpoint[i - 1]); 191 | } else { 192 | break; 193 | } 194 | } 195 | // Umount extra_ro_mountpoint. 196 | for (int i = 1; true; i += 2) { 197 | if (container->extra_ro_mountpoint[i] != NULL) { 198 | strcpy(to_umountpoint, container_dir); 199 | strcat(to_umountpoint, container->extra_ro_mountpoint[i]); 200 | for (int j = 0; j < 10; j++) { 201 | ruri_log("{base}Umounting %s\n", to_umountpoint); 202 | umount2(to_umountpoint, MNT_DETACH); 203 | umount(to_umountpoint); 204 | usleep(20000); 205 | } 206 | // Remove the empty file we created for mounting files into container. 207 | // Not rmdir(), so directory will not be removed. 208 | remove(to_umountpoint); 209 | // Make ASAN happy. 210 | free(container->extra_mountpoint[i]); 211 | free(container->extra_mountpoint[i - 1]); 212 | } else { 213 | break; 214 | } 215 | } 216 | } 217 | // Force umount system runtime directories for 10 times. 218 | // Not necessary, but I think it's more secure. 219 | ruri_log("{base}Umounting %s\n", sys_dir); 220 | ruri_log("{base}Umounting %s\n", proc_dir); 221 | ruri_log("{base}Umounting %s\n", dev_dir); 222 | ruri_log("{base}Umounting %s\n", container_dir); 223 | for (int i = 1; i < 10; i++) { 224 | umount2(sys_dir, MNT_DETACH | MNT_FORCE); 225 | usleep(2000); 226 | umount2(dev_dir, MNT_DETACH | MNT_FORCE); 227 | usleep(2000); 228 | umount2(proc_dir, MNT_DETACH | MNT_FORCE); 229 | usleep(2000); 230 | umount2(container_dir, MNT_DETACH | MNT_FORCE); 231 | usleep(2000); 232 | } 233 | // Kill ns_pid. 234 | if (container->ns_pid > 0) { 235 | ruri_log("Kill ns pid: %d\n", container->ns_pid); 236 | kill(container->ns_pid, SIGKILL); 237 | } 238 | // Kill all processes in container. 239 | // For container with PID ns enabled, when ns_pid is killed, 240 | // all process will die, but without PID ns, we still need to 241 | // find & kill other process. 242 | ruri_kill_container(container_dir); 243 | // Make Asan happy. 244 | free(container); 245 | // Use info in /proc/mounts to umount container. 246 | // This is a double check. 247 | umount_subdir(container_dir); 248 | } 249 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | all : 2 | @echo "\033[33mWarning: This test is under sudo, do not run it on your device.\033[0m" 3 | @echo "\033[33mYou have 5 seconds to press Ctrl+C to cancel.\033[0m" 4 | @sleep 5 5 | @cc -o test-root test-root.c 6 | @sudo ./test-root -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | Hope it works. 2 | These tests were written when ruri was unstable, and they can ensure that there's no serious bugs for each commit. -------------------------------------------------------------------------------- /test/check_flag.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source global.sh 3 | 4 | for i in $(mount | grep ${TMPDIR} | awk '{print $3}'); do 5 | umount -lvf $i >/dev/null 2>&1 6 | done 7 | cd ${TMPDIR} 8 | ./ruri -U ./test 9 | ./ruri -U ./aarch64 10 | ./ruri -U ./armhf 11 | echo -e "${BASE}Remove ${TMPDIR}" 12 | rm -rf ${TMPDIR} 13 | -------------------------------------------------------------------------------- /test/global.sh: -------------------------------------------------------------------------------- 1 | export BASE="\033[1;38;2;254;228;208m" 2 | export RED="\033[31m" 3 | export GREEN="\033[32m" 4 | export YELLOW="\033[33m" 5 | export BLUE="\033[34m" 6 | export PURPLE="\033[35m" 7 | export CYAN="\033[36m" 8 | export CLEAR="\033[0m" 9 | 10 | function error() { 11 | cd ${TEST_ROOT} 12 | source clean.sh 13 | echo -e "${RED}\nFailed on: ${GREEN} #${TEST_NO}" 14 | echo -e "${RED}Description: ${CYAN} ${DESCRIPTION}" 15 | echo -e "${RED}Subtest: ${CYAN} ${SUBTEST_NO}" 16 | echo -e "${RED}Subtest Description: ${CYAN} ${SUBTEST_DESCRIPTION}" 17 | echo -e "${CLEAR}\n" 18 | printf "${RED}$@\n${CLEAR}" 19 | exit 1 20 | } 21 | 22 | function check_if_succeed() { 23 | if [[ $1 == "0" ]]; then 24 | return 0 25 | elif [[ $1 == "114" ]]; then 26 | error "ruri panic signal found!" 27 | else 28 | error "failed!" 29 | fi 30 | } 31 | 32 | function check_if_failed() { 33 | if [[ $1 == "0" ]]; then 34 | error "succeed, but expected failed!" 35 | else 36 | return 0 37 | fi 38 | } 39 | 40 | function show_test_description() { 41 | echo -e "${BASE}\nRunning test ${GREEN}#${TEST_NO}" 42 | echo -e "${BASE}Description:\n${CYAN} ${DESCRIPTION}" 43 | printf "${CLEAR}\n" 44 | sleep 2 45 | } 46 | 47 | function show_subtest_description() { 48 | echo -e "${BASE}\nRunning subtest ${GREEN}#${SUBTEST_NO} ${BASE}of test ${GREEN}#${TEST_NO}" 49 | echo -e "${BASE}Subtest Description:\n${CYAN} ${SUBTEST_DESCRIPTION}" 50 | printf "${CLEAR}\n" 51 | sleep 2 52 | } 53 | 54 | function pass_test() { 55 | echo -e "${BASE}\nPassed test${GREEN} #${TEST_NO}" 56 | printf "${CLEAR}\n" 57 | } 58 | 59 | function pass_subtest() { 60 | echo -e "${BASE}\nPassed subtest${GREEN} #${SUBTEST_NO} ${BASE}of test ${GREEN}#${TEST_NO}" 61 | printf "${CLEAR}\n" 62 | } 63 | -------------------------------------------------------------------------------- /test/init-root-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source global.sh 3 | 4 | export TEST_NO=0 5 | export DESCRIPTION="This script is used to create files and directories for testing." 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Build ruri" 10 | export TMPDIR=tmpdir-$RANDOM 11 | show_subtest_description 12 | cd .. 13 | mkdir ${TMPDIR} 14 | check_if_succeed $? 15 | export TMPDIR=$(realpath ${TMPDIR}) 16 | aclocal 17 | autoconf 18 | ./configure --enable-debug --enable-dev 19 | check_if_succeed $? 20 | make 21 | check_if_succeed $? 22 | mv ruri ${TMPDIR} 23 | check_if_succeed $? 24 | pass_subtest 25 | 26 | export SUBTEST_NO=2 27 | export SUBTEST_DESCRIPTION="Check ruri" 28 | show_subtest_description 29 | cd ${TMPDIR} 30 | ./ruri -v 31 | check_if_succeed $? 32 | pass_subtest 33 | 34 | export SUBTEST_NO=3 35 | export SUBTEST_DESCRIPTION="Get rootfs.tar.xz" 36 | show_subtest_description 37 | git clone https://github.com/moe-hacker/rootfstool 38 | check_if_succeed $? 39 | rootfstool/rootfstool d -d alpine -v edge 40 | check_if_succeed $? 41 | pass_subtest 42 | 43 | export SUBTEST_NO=4 44 | export SUBTEST_DESCRIPTION="Create test.img as rootfs" 45 | show_subtest_description 46 | if [[ -e /tmp/test ]]; then 47 | rm /tmp/test 48 | fi 49 | if [[ -d /tmp/test ]]; then 50 | ./ruri -U /tmp/test 51 | rm -rf /tmp/test 52 | fi 53 | dd if=/dev/zero of=test.img bs=1M count=256 54 | check_if_succeed $? 55 | mkfs.ext4 test.img 56 | check_if_succeed $? 57 | mkdir /tmp/test 58 | check_if_succeed $? 59 | LOOPFILE=$(losetup -f) 60 | losetup ${LOOPFILE} ./test.img 61 | mount ${LOOPFILE} /tmp/test 62 | check_if_succeed $? 63 | tar -xf rootfs.tar.xz -C /tmp/test 64 | check_if_succeed $? 65 | umount -lvf /tmp/test 66 | check_if_succeed $? 67 | pass_subtest 68 | 69 | export SUBTEST_NO=5 70 | export SUBTEST_DESCRIPTION="Create test2.img as extra mountpoint" 71 | show_subtest_description 72 | dd if=/dev/zero of=test2.img bs=1M count=256 73 | check_if_succeed $? 74 | mkfs.ext4 test2.img 75 | check_if_succeed $? 76 | LOOPFILE=$(losetup -f) 77 | losetup ${LOOPFILE} ./test2.img 78 | mount ${LOOPFILE} /tmp/test 79 | check_if_succeed $? 80 | touch /tmp/test/test.txt 81 | check_if_succeed $? 82 | echo "x" >/tmp/test/test.txt 83 | check_if_succeed $? 84 | umount -lvf /tmp/test 85 | check_if_succeed $? 86 | pass_subtest 87 | 88 | export SUBTEST_NO=6 89 | export SUBTEST_DESCRIPTION="Create ./test as rootfs" 90 | show_subtest_description 91 | mkdir test 92 | check_if_succeed $? 93 | tar -xf rootfs.tar.xz -C test 94 | check_if_succeed $? 95 | pass_subtest 96 | 97 | export SUBTEST_NO=7 98 | export SUBTEST_DESCRIPTION="Create ./aarch64 as aarch64 rootfs" 99 | show_subtest_description 100 | mkdir aarch64 101 | check_if_succeed $? 102 | rm rootfs.tar.xz || true 103 | rootfstool/rootfstool d -d alpine -v edge -a arm64 104 | check_if_succeed $? 105 | tar -xf rootfs.tar.xz -C aarch64 106 | check_if_succeed $? 107 | pass_subtest 108 | 109 | export SUBTEST_NO=8 110 | export SUBTEST_DESCRIPTION="Create ./armhf as armhf rootfs" 111 | show_subtest_description 112 | mkdir armhf 113 | check_if_succeed $? 114 | rm rootfs.tar.xz || true 115 | rootfstool/rootfstool d -d alpine -v edge -a armhf 116 | check_if_succeed $? 117 | tar -xf rootfs.tar.xz -C armhf 118 | check_if_succeed $? 119 | pass_subtest 120 | 121 | pass_test 122 | -------------------------------------------------------------------------------- /test/root/1-basic.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=1 5 | export DESCRIPTION="This is ruri basic test" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Chroot container with no args" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | ./ruri ./test /bin/echo -e "${BASE}==> Running echo command in container" 13 | check_if_succeed $? 14 | if ! mountpoint -q ./test/sys; then 15 | error "Seems that container did not mounted properly!" 16 | fi 17 | pass_subtest 18 | 19 | export SUBTEST_NO=2 20 | export SUBTEST_DESCRIPTION="Umount container" 21 | show_subtest_description 22 | cd ${TMPDIR} 23 | ./ruri -U ./test 24 | check_if_succeed $? 25 | if mountpoint -q ./test/sys; then 26 | error "Seems that container did not unmounted properly!" 27 | fi 28 | if mountpoint -q ./test/dev; then 29 | error "Seems that container did not unmounted properly!" 30 | fi 31 | if mountpoint -q ./test/proc; then 32 | error "Seems that container did not unmounted properly!" 33 | fi 34 | if mountpoint -q ./test; then 35 | error "Seems that container did not unmounted properly!" 36 | fi 37 | echo -e "${BASE}==> Container unmounted successfully" 38 | pass_subtest 39 | 40 | export SUBTEST_NO=3 41 | export SUBTEST_DESCRIPTION="Chroot container with -m option" 42 | show_subtest_description 43 | cd ${TMPDIR} 44 | ./ruri -m /tmp /tm ./test /bin/echo -e "${BASE}==> Running echo command in container" 45 | check_if_succeed $? 46 | if ! mountpoint -q ./test/tm; then 47 | error "mount /tmp to /tm failed!" 48 | fi 49 | echo -e "${BASE}==> /tmp mount to /tm successfully" 50 | pass_subtest 51 | 52 | export SUBTEST_NO=4 53 | export SUBTEST_DESCRIPTION="Umount container with extra mountpoint /tm" 54 | show_subtest_description 55 | cd ${TMPDIR} 56 | ./ruri -U ./test 57 | check_if_succeed $? 58 | if mountpoint -q ./test/tm; then 59 | error "Umount /tm failed!" 60 | fi 61 | echo -e "${BASE}==> umount /tm successfully" 62 | pass_subtest 63 | 64 | export SUBTEST_NO=5 65 | export SUBTEST_DESCRIPTION="Test unshare container" 66 | show_subtest_description 67 | cd ${TMPDIR} 68 | ./ruri -u ./test dd if=/dev/zero of=/nullfile bs=1M count=1 69 | if [[ ! -e test/nullfile ]]; then 70 | error "File /nullfile does not exist!" 71 | fi 72 | if [[ $(stat -c %s test/nullfile) -ne 1048576 ]]; then 73 | error "File /nullfile size is not 1M!" 74 | fi 75 | echo -e "${BASE}==> unshare container create /nullfile in container successfully" 76 | pass_subtest 77 | 78 | pass_test 79 | -------------------------------------------------------------------------------- /test/root/2-container_runtime.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=2 5 | export DESCRIPTION="Test if /proc, /sys and /dev works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="/dev/zero" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | cat <test/test.sh 13 | dd if=/dev/zero of=/nullfile bs=1M count=1 14 | EOF 15 | chmod 777 test/test.sh 16 | ./ruri ./test /bin/sh /test.sh 17 | check_if_succeed $? 18 | if [[ ! -e test/nullfile ]]; then 19 | error "File /nullfile does not exist!" 20 | fi 21 | if [[ $(stat -c %s test/nullfile) -ne 1048576 ]]; then 22 | error "File /nullfile size is not 1M!" 23 | fi 24 | echo -e "${BASE}==> /dev/zero works properly" 25 | ./ruri -U ./test 26 | pass_subtest 27 | 28 | export SUBTEST_NO=2 29 | export SUBTEST_DESCRIPTION="/dev/urandom" 30 | show_subtest_description 31 | cd ${TMPDIR} 32 | cat <test/test.sh 33 | dd if=/dev/urandom of=/randomfile bs=1M count=1 34 | EOF 35 | chmod 777 test/test.sh 36 | ./ruri ./test /bin/sh /test.sh 37 | check_if_succeed $? 38 | if [[ ! -e test/randomfile ]]; then 39 | error "File /randomfile does not exist!" 40 | fi 41 | if [[ $(stat -c %s test/randomfile) -ne 1048576 ]]; then 42 | error "File /randomfile size is not 1M!" 43 | fi 44 | echo -e "${BASE}==> /dev/urandom works properly" 45 | ./ruri -U ./test 46 | pass_subtest 47 | 48 | export SUBTEST_NO=3 49 | export SUBTEST_DESCRIPTION="/dev/null" 50 | show_subtest_description 51 | cd ${TMPDIR} 52 | cat <test/test.sh 53 | if [ "$(echo "Hello World" >/dev/null)" != "" ];then 54 | exit 1 55 | fi 56 | exit 0 57 | EOF 58 | chmod 777 test/test.sh 59 | ./ruri ./test /bin/sh /test.sh 60 | check_if_succeed $? 61 | echo -e "${BASE}==> /dev/null works properly" 62 | ./ruri -U ./test 63 | pass_subtest 64 | 65 | export SUBTEST_NO=4 66 | export SUBTEST_DESCRIPTION="/dev/stdout" 67 | show_subtest_description 68 | cd ${TMPDIR} 69 | cat <test/test.sh 70 | if [ "$(echo "Hello World" >/dev/stdout)" != "Hello World" ];then 71 | exit 1 72 | fi 73 | exit 0 74 | EOF 75 | chmod 777 test/test.sh 76 | ./ruri ./test /bin/sh /test.sh 77 | check_if_succeed $? 78 | echo -e "${BASE}==> /dev/stdout works properly" 79 | ./ruri -U ./test 80 | pass_subtest 81 | 82 | export SUBTEST_NO=5 83 | export SUBTEST_DESCRIPTION="/proc/self/status" 84 | show_subtest_description 85 | cd ${TMPDIR} 86 | ./ruri ./test /bin/cat /proc/self/status 87 | check_if_succeed $? 88 | echo -e "${BASE}==> /proc/self/status works properly" 89 | ./ruri -U ./test 90 | pass_subtest 91 | 92 | export SUBTEST_NO=6 93 | export SUBTEST_DESCRIPTION="/proc/$$/status" 94 | show_subtest_description 95 | cd ${TMPDIR} 96 | ./ruri ./test /bin/cat /proc/$$/status 97 | check_if_succeed $? 98 | echo -e "${BASE}==> /proc/$$/status works properly" 99 | ./ruri -U ./test 100 | pass_subtest 101 | 102 | export SUBTEST_NO=7 103 | export SUBTEST_DESCRIPTION="masked dirs, in privileged container" 104 | show_subtest_description 105 | cd ${TMPDIR} 106 | cat <test/test.sh 107 | echo 1048576 > /proc/sys/fs/pipe-max-size 108 | EOF 109 | chmod 777 test/test.sh 110 | ./ruri -p ./test /bin/sh /test.sh 111 | check_if_failed $? 112 | echo -e "${BASE}==> /proc/sys/fs/pipe-max-size is masked" 113 | ./ruri -U ./test 114 | pass_subtest 115 | 116 | pass_test 117 | -------------------------------------------------------------------------------- /test/root/3-capability.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=3 5 | export DESCRIPTION="Test if capability control works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Default capability" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | cat <test/test.sh 13 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 14 | EOF 15 | chmod 777 test/test.sh 16 | ./ruri ./test /bin/sh /test.sh 17 | check_if_succeed $? 18 | if [[ "$(capsh --decode=$(cat test/cap) | grep cap_sys_admin)" != "" ]]; then 19 | error "Found cap_sys_admin, default capabitily is not set properly!" 20 | fi 21 | echo -e "${BASE}==> Default capability works properly" 22 | ./ruri -U ./test 23 | pass_subtest 24 | 25 | export SUBTEST_NO=2 26 | export SUBTEST_DESCRIPTION="Capability limit of mknod()" 27 | show_subtest_description 28 | cd ${TMPDIR} 29 | cat <test/test.sh 30 | mknod /dev/n c 1 3 31 | EOF 32 | chmod 777 test/test.sh 33 | ./ruri ./test /bin/sh /test.sh 34 | check_if_failed $? 35 | echo -e "${BASE}==> mknod() is not allowed" 36 | ./ruri -U ./test 37 | pass_subtest 38 | 39 | export SUBTEST_NO=3 40 | export SUBTEST_DESCRIPTION="Capability limit of mount()" 41 | show_subtest_description 42 | cd ${TMPDIR} 43 | cat <test/test.sh 44 | mount -t tmpfs none /mnt 45 | EOF 46 | chmod 777 test/test.sh 47 | ./ruri ./test /bin/sh /test.sh 48 | check_if_failed $? 49 | echo -e "${BASE}==> mount() is not allowed" 50 | ./ruri -U ./test 51 | pass_subtest 52 | 53 | export SUBTEST_NO=4 54 | export SUBTEST_DESCRIPTION="Add capability CAP_SYS_ADMIN" 55 | show_subtest_description 56 | cd ${TMPDIR} 57 | cat <test/test.sh 58 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 59 | EOF 60 | chmod 777 test/test.sh 61 | ./ruri -k cap_sys_admin ./test /bin/sh /test.sh 62 | check_if_succeed $? 63 | if [[ "$(capsh --decode=$(cat test/cap) | grep cap_sys_admin)" == "" ]]; then 64 | error "Keep cap cap_sys_admin is not set properly!" 65 | fi 66 | echo -e "${BASE}==> Keep cap cap_sys_admin works properly" 67 | ./ruri -U ./test 68 | pass_subtest 69 | 70 | export SUBTEST_NO=5 71 | export SUBTEST_DESCRIPTION="Drop capability CAP_CHOWN" 72 | show_subtest_description 73 | cd ${TMPDIR} 74 | cat <test/test.sh 75 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 76 | EOF 77 | chmod 777 test/test.sh 78 | ./ruri -d cap_chown ./test /bin/sh /test.sh 79 | check_if_succeed $? 80 | if [[ "$(capsh --decode=$(cat test/cap) | grep cap_chown)" != "" ]]; then 81 | error "Drop cap cap_chown is not set properly!" 82 | fi 83 | echo -e "${BASE}==> Drop cap cap_chown works properly" 84 | ./ruri -U ./test 85 | pass_subtest 86 | 87 | export SUBTEST_NO=6 88 | export SUBTEST_DESCRIPTION="Drop all capabilities" 89 | show_subtest_description 90 | cd ${TMPDIR} 91 | cat <test/test.sh 92 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 93 | EOF 94 | chmod 777 test/test.sh 95 | for i in $(seq 0 40); do 96 | DROP_CAP="$DROP_CAP -d $i" 97 | done 98 | ./ruri $DROP_CAP ./test /bin/sh /test.sh 99 | check_if_succeed $? 100 | if [[ "$(capsh --decode=$(cat test/cap) | cut -d '=' -f 2)" != "" ]]; then 101 | error "Cannot drop all capabilities!" 102 | fi 103 | echo -e "${BASE}==> Drop all capabilities works properly" 104 | ./ruri -U ./test 105 | pass_subtest 106 | 107 | export SUBTEST_NO=7 108 | export SUBTEST_DESCRIPTION="Keep all capabilities(privileged)" 109 | show_subtest_description 110 | cd ${TMPDIR} 111 | cat <test/test.sh 112 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 113 | EOF 114 | chmod 777 test/test.sh 115 | ./ruri -p ./test /bin/sh /test.sh 116 | check_if_succeed $? 117 | if [[ "$(capsh --decode=$(cat test/cap) | grep "cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog")" == "" ]]; then 118 | error "Cannot keep all capabilities!" 119 | fi 120 | echo -e "${BASE}==> Keep all capabilities works properly" 121 | ./ruri -U ./test 122 | pass_subtest 123 | 124 | export SUBTEST_NO=8 125 | export SUBTEST_DESCRIPTION="Drop all capabilities but keep CAP_SYS_ADMIN" 126 | show_subtest_description 127 | cd ${TMPDIR} 128 | cat <test/test.sh 129 | cat /proc/self/status | grep CapBnd| awk '{print \$2}' > /cap 130 | EOF 131 | chmod 777 test/test.sh 132 | for i in $(seq 0 40); do 133 | DROP_CAP="$DROP_CAP -d $i" 134 | done 135 | ./ruri $DROP_CAP -k cap_sys_admin ./test /bin/sh /test.sh 136 | check_if_succeed $? 137 | if [[ "$(capsh --decode=$(cat test/cap) | cut -d '=' -f 2)" != "cap_sys_admin" ]]; then 138 | error "Cannot drop all capabilities except CAP_SYS_ADMIN!" 139 | fi 140 | echo -e "${BASE}==> Drop all capabilities but keep CAP_SYS_ADMIN works properly" 141 | ./ruri -U ./test 142 | pass_subtest 143 | 144 | pass_test 145 | -------------------------------------------------------------------------------- /test/root/4-mount.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=4 5 | export DESCRIPTION="Test if mount function works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Mount test.img as rootfs" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | mkdir ./t >/dev/null 2>&1 13 | ./ruri -m test.img / ./t /bin/echo -e "${BASE}==> Running echo command in container" 14 | check_if_succeed $? 15 | if [[ ! -e ./t/bin/sh ]]; then 16 | error "Seems that container did not mounted properly!" 17 | fi 18 | echo -e "${BASE}==> test.img mounted successfully" 19 | ./ruri -U ./t 20 | pass_subtest 21 | 22 | export SUBTEST_NO=2 23 | export SUBTEST_DESCRIPTION="Mount ./test2.img as /m" 24 | show_subtest_description 25 | cd ${TMPDIR} 26 | mkdir ./t >/dev/null 2>&1 27 | ./ruri -m test.img / -m test2.img /m ./t /bin/echo -e "${BASE}==> Running echo command in container" 28 | check_if_succeed $? 29 | if [[ ! -e ./t/bin/sh ]]; then 30 | error "Seems that container did not mounted properly!" 31 | fi 32 | if [[ "$(cat ./t/m/test.txt)" != "x" ]]; then 33 | error "test2.img does not mounted properly!" 34 | fi 35 | echo -e "${BASE}==> ./test2.img mounted as /m successfully" 36 | ./ruri -U ./t 37 | pass_subtest 38 | 39 | export SUBTEST_NO=3 40 | export SUBTEST_DESCRIPTION="Mount ./test2.img as /m and /n" 41 | show_subtest_description 42 | cd ${TMPDIR} 43 | mkdir ./t >/dev/null 2>&1 44 | ./ruri -m test.img / -m test2.img /m -m test2.img /n ./t /bin/echo -e "${BASE}==> Running echo command in container" 45 | check_if_succeed $? 46 | if [[ ! -e ./t/bin/sh ]]; then 47 | error "Seems that container did not mounted properly!" 48 | fi 49 | if [[ "$(cat ./t/m/test.txt)" != "x" ]]; then 50 | error "test2.img does not mounted properly!" 51 | fi 52 | if [[ "$(cat ./t/n/test.txt)" != "x" ]]; then 53 | error "test2.img does not mounted properly!" 54 | fi 55 | echo -e "${BASE}==> ./test2.img mounted as /m and /n successfully" 56 | ./ruri -U ./t 57 | pass_subtest 58 | 59 | export SUBTEST_NO=4 60 | export SUBTEST_DESCRIPTION="Mount ./test3 as /t" 61 | show_subtest_description 62 | cd ${TMPDIR} 63 | mkdir ./test3 >/dev/null 2>&1 64 | echo x >test3/test.txt 65 | ./ruri -m ./test3 /t ./test /bin/echo -e "${BASE}==> Running echo command in container" 66 | if [[ "$(cat ./test/t/test.txt)" != "x" ]]; then 67 | error "test3 does not mounted properly!" 68 | fi 69 | echo -e "${BASE}==> ./test3 mounted as /t successfully" 70 | ./ruri -U ./test 71 | pass_subtest 72 | 73 | pass_test 74 | -------------------------------------------------------------------------------- /test/root/5-ro_mount.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=5 5 | export DESCRIPTION="Test if read-only mount function works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Mount test.img as read-only rootfs" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | mkdir ./t >/dev/null 2>&1 13 | ./ruri -M test.img / ./t touch /test.txt 14 | check_if_failed $? 15 | if [[ ! -e ./t/bin/sh ]]; then 16 | error "Seems that container did not mounted properly!" 17 | fi 18 | if [[ -e ./t/test.txt ]]; then 19 | error "Seems that container is not read-only!" 20 | fi 21 | echo -e "${BASE}==> test.img mounted as read-only successfully" 22 | ./ruri -U ./t 23 | pass_subtest 24 | 25 | export SUBTEST_NO=2 26 | export SUBTEST_DESCRIPTION="Mount / as read-only rootfs (-R)" 27 | show_subtest_description 28 | cd ${TMPDIR} 29 | ./ruri -R ./test touch /test.txt 30 | check_if_failed $? 31 | if [[ -e ./test/test.txt ]]; then 32 | error "Seems that container is not read-only!" 33 | fi 34 | echo -e "${BASE}==> / mounted as read-only successfully" 35 | ./ruri -U ./test 36 | pass_subtest 37 | 38 | pass_test 39 | -------------------------------------------------------------------------------- /test/root/6-env.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=6 5 | export DESCRIPTION="Test if -e option works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Environment variable test" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | cat <test/test.sh 13 | #!/bin/sh 14 | if [ "\$x" == "xxxx" ];then 15 | exit 0 16 | else 17 | exit 1 18 | fi 19 | EOF 20 | chmod +x test/test.sh 21 | ./ruri -e x xxxx ./test /bin/sh /test.sh 22 | check_if_succeed $? 23 | echo -e "${BASE}==> Environment variable test \$x=xxxx passed!${CLEAR}\n" 24 | ./ruri -U ./test 25 | pass_subtest 26 | 27 | export SUBTEST_NO=2 28 | export SUBTEST_DESCRIPTION="Environment variable test" 29 | show_subtest_description 30 | cd ${TMPDIR} 31 | cat <test/test.sh 32 | #!/bin/sh 33 | if [ "\$x" != "xxxx" ];then 34 | exit 1 35 | fi 36 | if [ "\$y" != "xxxx" ];then 37 | exit 1 38 | fi 39 | exit 0 40 | EOF 41 | chmod +x test/test.sh 42 | ./ruri -e x xxxx -e y xxxx ./test /bin/sh /test.sh 43 | check_if_succeed $? 44 | echo -e "${BASE}==> Environment variable test \$x=xxxx \$y=xxxx passed!${CLEAR}\n" 45 | ./ruri -U ./test 46 | pass_subtest 47 | 48 | pass_test 49 | -------------------------------------------------------------------------------- /test/root/7-ps.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=7 5 | export DESCRIPTION="Test if -P option works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="-P with common chroot container" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | cat <test/test.sh 13 | #!/bin/sh 14 | sleep 100 15 | EOF 16 | chmod +x test/test.sh 17 | ./ruri ./test /bin/sh /test.sh & 18 | check_if_succeed $? 19 | sleep 1 20 | if [[ "$(./ruri -P ./test)" == "" ]]; then 21 | error "ruri -P has no output" 22 | fi 23 | ./ruri -P ./test 24 | echo -e "${BASE}==> -P for common chroot container passed!${CLEAR}\n" 25 | ./ruri -U ./test 26 | pass_subtest 27 | 28 | cd ${TMPDIR} 29 | ./ruri -P ./test | awk '{print $1}' | xargs kill -9 30 | 31 | export SUBTEST_NO=2 32 | export SUBTEST_DESCRIPTION="-P with stopped container" 33 | show_subtest_description 34 | cd ${TMPDIR} 35 | if [[ "$(./ruri -P ./test)" != "" ]]; then 36 | error "ruri -P has output, expect not!" 37 | fi 38 | echo -e "${BASE}==> -P for stopped container passed!${CLEAR}\n" 39 | ./ruri -U ./test 40 | pass_subtest 41 | 42 | pass_test 43 | -------------------------------------------------------------------------------- /test/root/8-secure-option.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=8 5 | export DESCRIPTION="Test if secure options works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="No new privs" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | cat <test/test.sh 13 | cat /proc/self/status | grep NoNewPrivs| awk '{print \$2}' > /priv 14 | EOF 15 | chmod 777 test/test.sh 16 | ./ruri -n ./test /bin/sh /test.sh 17 | check_if_succeed $? 18 | if [[ "$(cat test/priv)" != "1" ]]; then 19 | error "Cannot set no_new_privs!" 20 | fi 21 | echo -e "${BASE}==> No new privs works properly" 22 | ./ruri -U ./test 23 | pass_subtest 24 | 25 | export SUBTEST_NO=2 26 | export SUBTEST_DESCRIPTION="Seccomp" 27 | show_subtest_description 28 | cd ${TMPDIR} 29 | cat <test/test.sh 30 | cat /proc/self/status | grep Seccomp| awk '{print \$2}' > /seccomp 31 | EOF 32 | chmod 777 test/test.sh 33 | ./ruri -n ./test /bin/sh /test.sh 34 | check_if_succeed $? 35 | if [[ "$(cat test/seccomp)" == "0" ]]; then 36 | error "Cannot set Seccomp!" 37 | fi 38 | echo -e "${BASE}==> Seccomp works properly" 39 | ./ruri -U ./test 40 | pass_subtest 41 | 42 | export SUBTEST_NO=3 43 | export SUBTEST_DESCRIPTION="Just chroot" 44 | show_subtest_description 45 | cd ${TMPDIR} 46 | cat <test/test.sh 47 | cat /proc/self/status 48 | EOF 49 | chmod 777 test/test.sh 50 | ./ruri -j ./test /bin/sh /test.sh 51 | check_if_failed $? 52 | echo -e "${BASE}==> Just chroot works properly" 53 | ./ruri -U ./test 54 | pass_subtest 55 | 56 | export SUBTEST_NO=4 57 | export SUBTEST_DESCRIPTION="No rurienv" 58 | show_subtest_description 59 | cd ${TMPDIR} 60 | cat <test/test.sh 61 | cat /.rurienv 62 | EOF 63 | chmod 777 test/test.sh 64 | ./ruri -N ./test /bin/sh /test.sh 65 | check_if_failed $? 66 | echo -e "${BASE}==> No rurienv works properly" 67 | ./ruri -U ./test 68 | pass_subtest 69 | 70 | export SUBTEST_NO=5 71 | export SUBTEST_DESCRIPTION="Mount host runtime" 72 | show_subtest_description 73 | cd ${TMPDIR} 74 | cat <test/test.sh 75 | cat /proc/mounts | grep /proc/block 76 | EOF 77 | chmod 777 test/test.sh 78 | ./ruri -S ./test /bin/sh /test.sh 79 | check_if_failed $? 80 | echo -e "${BASE}==> Mount host runtime works properly" 81 | ./ruri -U ./test 82 | pass_subtest 83 | 84 | pass_test 85 | -------------------------------------------------------------------------------- /test/root/9-cross_arch.sh: -------------------------------------------------------------------------------- 1 | cd ${TEST_ROOT} 2 | source global.sh 3 | 4 | export TEST_NO=9 5 | export DESCRIPTION="Test if cross-arch container works properly" 6 | show_test_description 7 | 8 | export SUBTEST_NO=1 9 | export SUBTEST_DESCRIPTION="Run aarch64 container on x86_64" 10 | show_subtest_description 11 | cd ${TMPDIR} 12 | ./ruri -a aarch64 -q /usr/bin/qemu-aarch64-static ./aarch64 dd if=/dev/zero of=/test bs=1M count=1 13 | check_if_succeed $? 14 | if [[ ! -e aarch64/test ]]; then 15 | error "Cannot run aarch64 container on x86_64!" 16 | fi 17 | echo -e "${BASE}==> Run aarch64 container on x86_64 works properly" 18 | ./ruri -U ./aarch64 19 | pass_subtest 20 | 21 | export SUBTEST_NO=2 22 | export SUBTEST_DESCRIPTION="Run armhf container on x86_64" 23 | show_subtest_description 24 | cd ${TMPDIR} 25 | ./ruri -a armhf -q /usr/bin/qemu-arm-static ./armhf dd if=/dev/zero of=/test bs=1M count=1 26 | check_if_succeed $? 27 | if [[ ! -e armhf/test ]]; then 28 | error "Cannot run armhf container on x86_64!" 29 | fi 30 | echo -e "${BASE}==> Run armhf container on x86_64 works properly" 31 | ./ruri -U ./armhf 32 | pass_subtest 33 | 34 | pass_test 35 | -------------------------------------------------------------------------------- /test/test-root.c: -------------------------------------------------------------------------------- 1 | #include "../src/include/ruri.h" 2 | int main() 3 | { 4 | // Timeout: 400s 5 | // This file will ensure that the test will not stuck. 6 | pid_t pid = fork(); 7 | if (pid > 0) { 8 | int status; 9 | for (int i = 0; i < 400; i++) { 10 | sleep(1); 11 | if (waitpid(pid, &status, WNOHANG) == pid) { 12 | exit(status); 13 | } 14 | } 15 | if (waitpid(pid, &status, WNOHANG) == 0) { 16 | kill(pid, SIGKILL); 17 | printf("Timeout\n"); 18 | exit(114); 19 | } 20 | exit(0); 21 | } 22 | char *command[] = { "bash", "test-root.sh", NULL }; 23 | execvp(command[0], command); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /test/test-root.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source global.sh 3 | 4 | # This will set $TEST_ROOT 5 | export TEST_ROOT=$(pwd) 6 | 7 | DESCRIPTION="This is a test script that runs all test scripts." 8 | echo -e "${BASE}Running $0" 9 | echo -e "${BASE}Description:\n${CYAN} ${DESCRIPTION}" 10 | printf "${CLEAR}" 11 | sleep 1 12 | 13 | # This will set $TMPDIR 14 | cd ${TEST_ROOT} 15 | source init-root-test.sh 16 | check_if_succeed $? 17 | 18 | # Do all tests 19 | cd ${TEST_ROOT} 20 | for i in $(ls root/*.sh); do 21 | cd ${TEST_ROOT} 22 | source $i 23 | check_if_succeed $? 24 | done 25 | 26 | cd ${TMPDIR} 27 | rm ruri 28 | wget -O - https://github.com/Moe-hacker/ruri/raw/refs/heads/main/get-ruri.sh | bash -s -- -s 29 | check_if_succeed $? 30 | 31 | # Check for released version 32 | cd ${TEST_ROOT} 33 | for i in $(ls root/*.sh); do 34 | cd ${TEST_ROOT} 35 | source $i 36 | check_if_succeed $? 37 | done 38 | 39 | # Clean up 40 | cd ${TEST_ROOT} 41 | source clean.sh 42 | 43 | echo -e "${BASE}Passed all root tests!${CLEAR}" 44 | --------------------------------------------------------------------------------