├── .clang-format ├── .clang-tidy ├── .devcontainer └── devcontainer.json ├── .github ├── dependabot.yml └── workflows │ └── c.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── Makefile ├── README.md ├── docs └── barco.png ├── include ├── cgroups.h ├── container.h ├── mount.h ├── sec.h └── user.h ├── lib ├── argtable │ ├── argtable3.c │ └── argtable3.h └── log │ ├── log.c │ └── log.h ├── src ├── barco.c ├── cgroups.c ├── container.c ├── mount.c ├── sec.c └── user.c └── tests └── barco_test.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveAssignments: 8 | Enabled: false 9 | AcrossEmptyLines: false 10 | AcrossComments: false 11 | AlignCompound: false 12 | PadOperators: true 13 | AlignConsecutiveBitFields: 14 | Enabled: false 15 | AcrossEmptyLines: false 16 | AcrossComments: false 17 | AlignCompound: false 18 | PadOperators: false 19 | AlignConsecutiveDeclarations: 20 | Enabled: false 21 | AcrossEmptyLines: false 22 | AcrossComments: false 23 | AlignCompound: false 24 | PadOperators: false 25 | AlignConsecutiveMacros: 26 | Enabled: false 27 | AcrossEmptyLines: false 28 | AcrossComments: false 29 | AlignCompound: false 30 | PadOperators: false 31 | AlignConsecutiveShortCaseStatements: 32 | Enabled: false 33 | AcrossEmptyLines: false 34 | AcrossComments: false 35 | AlignCaseColons: false 36 | AlignEscapedNewlines: Right 37 | AlignOperands: Align 38 | AlignTrailingComments: 39 | Kind: Always 40 | OverEmptyLines: 0 41 | AllowAllArgumentsOnNextLine: true 42 | AllowAllParametersOfDeclarationOnNextLine: true 43 | AllowShortBlocksOnASingleLine: Never 44 | AllowShortCaseLabelsOnASingleLine: false 45 | AllowShortEnumsOnASingleLine: true 46 | AllowShortFunctionsOnASingleLine: All 47 | AllowShortIfStatementsOnASingleLine: Never 48 | AllowShortLambdasOnASingleLine: All 49 | AllowShortLoopsOnASingleLine: false 50 | AlwaysBreakAfterDefinitionReturnType: None 51 | AlwaysBreakAfterReturnType: None 52 | AlwaysBreakBeforeMultilineStrings: false 53 | AlwaysBreakTemplateDeclarations: MultiLine 54 | AttributeMacros: 55 | - __capability 56 | BinPackArguments: true 57 | BinPackParameters: true 58 | BitFieldColonSpacing: Both 59 | BraceWrapping: 60 | AfterCaseLabel: false 61 | AfterClass: false 62 | AfterControlStatement: Never 63 | AfterEnum: false 64 | AfterExternBlock: false 65 | AfterFunction: false 66 | AfterNamespace: false 67 | AfterObjCDeclaration: false 68 | AfterStruct: false 69 | AfterUnion: false 70 | BeforeCatch: false 71 | BeforeElse: false 72 | BeforeLambdaBody: false 73 | BeforeWhile: false 74 | IndentBraces: false 75 | SplitEmptyFunction: true 76 | SplitEmptyRecord: true 77 | SplitEmptyNamespace: true 78 | BreakAfterAttributes: Never 79 | BreakAfterJavaFieldAnnotations: false 80 | BreakArrays: true 81 | BreakBeforeBinaryOperators: None 82 | BreakBeforeConceptDeclarations: Always 83 | BreakBeforeBraces: Attach 84 | BreakBeforeInlineASMColon: OnlyMultiline 85 | BreakBeforeTernaryOperators: true 86 | BreakConstructorInitializers: BeforeColon 87 | BreakInheritanceList: BeforeColon 88 | BreakStringLiterals: true 89 | ColumnLimit: 80 90 | CommentPragmas: '^ IWYU pragma:' 91 | CompactNamespaces: false 92 | ConstructorInitializerIndentWidth: 4 93 | ContinuationIndentWidth: 4 94 | Cpp11BracedListStyle: true 95 | DerivePointerAlignment: false 96 | DisableFormat: false 97 | EmptyLineAfterAccessModifier: Never 98 | EmptyLineBeforeAccessModifier: LogicalBlock 99 | ExperimentalAutoDetectBinPacking: false 100 | FixNamespaceComments: true 101 | ForEachMacros: 102 | - foreach 103 | - Q_FOREACH 104 | - BOOST_FOREACH 105 | IfMacros: 106 | - KJ_IF_MAYBE 107 | IncludeBlocks: Preserve 108 | IncludeCategories: 109 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 110 | Priority: 2 111 | SortPriority: 0 112 | CaseSensitive: false 113 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 114 | Priority: 3 115 | SortPriority: 0 116 | CaseSensitive: false 117 | - Regex: '.*' 118 | Priority: 1 119 | SortPriority: 0 120 | CaseSensitive: false 121 | IncludeIsMainRegex: '(Test)?$' 122 | IncludeIsMainSourceRegex: '' 123 | IndentAccessModifiers: false 124 | IndentCaseBlocks: false 125 | IndentCaseLabels: false 126 | IndentExternBlock: AfterExternBlock 127 | IndentGotoLabels: true 128 | IndentPPDirectives: None 129 | IndentRequiresClause: true 130 | IndentWidth: 2 131 | IndentWrappedFunctionNames: false 132 | InsertBraces: false 133 | InsertNewlineAtEOF: false 134 | InsertTrailingCommas: None 135 | IntegerLiteralSeparator: 136 | Binary: 0 137 | BinaryMinDigits: 0 138 | Decimal: 0 139 | DecimalMinDigits: 0 140 | Hex: 0 141 | HexMinDigits: 0 142 | JavaScriptQuotes: Leave 143 | JavaScriptWrapImports: true 144 | KeepEmptyLinesAtTheStartOfBlocks: true 145 | KeepEmptyLinesAtEOF: false 146 | LambdaBodyIndentation: Signature 147 | LineEnding: DeriveLF 148 | MacroBlockBegin: '' 149 | MacroBlockEnd: '' 150 | MaxEmptyLinesToKeep: 1 151 | NamespaceIndentation: None 152 | ObjCBinPackProtocolList: Auto 153 | ObjCBlockIndentWidth: 2 154 | ObjCBreakBeforeNestedBlockParam: true 155 | ObjCSpaceAfterProperty: false 156 | ObjCSpaceBeforeProtocolList: true 157 | PackConstructorInitializers: BinPack 158 | PenaltyBreakAssignment: 2 159 | PenaltyBreakBeforeFirstCallParameter: 19 160 | PenaltyBreakComment: 300 161 | PenaltyBreakFirstLessLess: 120 162 | PenaltyBreakOpenParenthesis: 0 163 | PenaltyBreakString: 1000 164 | PenaltyBreakTemplateDeclaration: 10 165 | PenaltyExcessCharacter: 1000000 166 | PenaltyIndentedWhitespace: 0 167 | PenaltyReturnTypeOnItsOwnLine: 60 168 | PointerAlignment: Right 169 | PPIndentWidth: -1 170 | QualifierAlignment: Leave 171 | ReferenceAlignment: Pointer 172 | ReflowComments: true 173 | RemoveBracesLLVM: false 174 | RemoveParentheses: Leave 175 | RemoveSemicolon: false 176 | RequiresClausePosition: OwnLine 177 | RequiresExpressionIndentation: OuterScope 178 | SeparateDefinitionBlocks: Leave 179 | ShortNamespaceLines: 1 180 | SortIncludes: CaseSensitive 181 | SortJavaStaticImport: Before 182 | SortUsingDeclarations: LexicographicNumeric 183 | SpaceAfterCStyleCast: false 184 | SpaceAfterLogicalNot: false 185 | SpaceAfterTemplateKeyword: true 186 | SpaceAroundPointerQualifiers: Default 187 | SpaceBeforeAssignmentOperators: true 188 | SpaceBeforeCaseColon: false 189 | SpaceBeforeCpp11BracedList: false 190 | SpaceBeforeCtorInitializerColon: true 191 | SpaceBeforeInheritanceColon: true 192 | SpaceBeforeJsonColon: false 193 | SpaceBeforeParens: ControlStatements 194 | SpaceBeforeParensOptions: 195 | AfterControlStatements: true 196 | AfterForeachMacros: true 197 | AfterFunctionDefinitionName: false 198 | AfterFunctionDeclarationName: false 199 | AfterIfMacros: true 200 | AfterOverloadedOperator: false 201 | AfterRequiresInClause: false 202 | AfterRequiresInExpression: false 203 | BeforeNonEmptyParentheses: false 204 | SpaceBeforeRangeBasedForLoopColon: true 205 | SpaceBeforeSquareBrackets: false 206 | SpaceInEmptyBlock: false 207 | SpacesBeforeTrailingComments: 1 208 | SpacesInAngles: Never 209 | SpacesInContainerLiterals: true 210 | SpacesInLineCommentPrefix: 211 | Minimum: 1 212 | Maximum: -1 213 | SpacesInParens: Never 214 | SpacesInParensOptions: 215 | InCStyleCasts: false 216 | InConditionalStatements: false 217 | InEmptyParentheses: false 218 | Other: false 219 | SpacesInSquareBrackets: false 220 | Standard: Latest 221 | StatementAttributeLikeMacros: 222 | - Q_EMIT 223 | StatementMacros: 224 | - Q_UNUSED 225 | - QT_REQUIRE_VERSION 226 | TabWidth: 8 227 | UseTab: Never 228 | VerilogBreakBetweenInstancePorts: true 229 | WhitespaceSensitiveMacros: 230 | - BOOST_PP_STRINGIZE 231 | - CF_SWIFT_NAME 232 | - NS_SWIFT_NAME 233 | - PP_STRINGIZE 234 | - STRINGIZE 235 | ... 236 | 237 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | ExtraArgs: [] 3 | HeaderFileExtensions: 4 | - h 5 | HeaderFilterRegex: "./include/*" 6 | ImplementationFileExtensions: 7 | - c 8 | FormatStyle: file 9 | Checks: "-*,\ 10 | bugprone-*,\ 11 | -bugprone-easily-swappable-parameters, \ 12 | -bugprone-assignment-in-if-condition, \ 13 | -bugprone-reserved-identifier, \ 14 | cert-*,\ 15 | -cert-msc30-c, \ 16 | -cert-msc32-c, \ 17 | -cert-msc50-cpp, \ 18 | -cert-msc51-cpp, \ 19 | -cert-dcl37-c, \ 20 | -cert-dcl51-cpp, 21 | clang-analyzer-*,\ 22 | -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,\ 23 | concurrency-*,\ 24 | -concurrency-mt-unsafe, \ 25 | linuxkernel-*,\ 26 | llvm-*,\ 27 | -llvm-header-guard, \ 28 | misc-*,\ 29 | modernize-*,\ 30 | -modernize-use-trailing-return-type, \ 31 | -modernize-use-using, \ 32 | performance-*,\ 33 | -performance-enum-size, \ 34 | portability-*,\ 35 | readability-*, \ 36 | -readability-identifier-length" 37 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "barco", 3 | "image": "mcr.microsoft.com/devcontainers/universal", 4 | "postCreateCommand": "make setup", 5 | "customizations": { 6 | "vscode": { 7 | "extensions": [ 8 | "13xforever.language-x86-64-assembly", 9 | "aaron-bond.better-comments", 10 | "bierner.emojisense", 11 | "ms-vscode.cpptools-extension-pack", 12 | "bierner.github-markdown-preview", 13 | "bierner.markdown-checkbox", 14 | "bierner.markdown-emoji", 15 | "bierner.markdown-footnotes", 16 | "bierner.markdown-mermaid", 17 | "bierner.markdown-preview-github-styles", 18 | "bierner.markdown-yaml-preamble", 19 | "christian-kohler.path-intellisense", 20 | "DavidAnson.vscode-markdownlint", 21 | "eamodio.gitlens", 22 | "equinusocio.vsc-material-theme-icons", 23 | "foxundermoon.shell-format", 24 | "GitHub.codespaces", 25 | "GitHub.copilot", 26 | "GitHub.github-vscode-theme", 27 | "GitHub.remotehub", 28 | "GitHub.vscode-codeql", 29 | "github.vscode-github-actions", 30 | "GitHub.vscode-pull-request-github", 31 | "llvm-vs-code-extensions.vscode-clangd", 32 | "ms-vscode.hexeditor", 33 | "ms-vscode.makefile-tools", 34 | "ms-vsliveshare.vsliveshare", 35 | "ms-vsliveshare.vsliveshare-pack", 36 | "pnp.polacode", 37 | "quicktype.quicktype", 38 | "redhat.vscode-yaml", 39 | "tombonnike.vscode-status-bar-format-toggle", 40 | "vadimcn.vscode-lldb", 41 | "waderyan.gitblame", 42 | "yzhang.markdown-all-in-one" 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/c.yml: -------------------------------------------------------------------------------- 1 | name: C Workflow 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: 7 | - '*' 8 | pull_request: 9 | branches: ["main"] 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Install dependencies 18 | run: make setup 19 | - name: Run Linter 20 | run: make lint 21 | - name: Run Tests 22 | run: make test 23 | - name: Run Compiler 24 | run: make 25 | - name: Run Valgrind 26 | continue-on-error: true 27 | run: make check 28 | - name: Create Release 29 | if: startsWith(github.ref, 'refs/tags/') 30 | uses: svenstaro/upload-release-action@v2 31 | with: 32 | repo_token: ${{ secrets.GITHUB_TOKEN }} 33 | file: bin/${{ github.event.repository.name }} 34 | asset_name: ${{ github.event.repository.name }}-linux-amd64 35 | tag: ${{ github.ref }} 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Barco 55 | build/ 56 | bin/ 57 | TODO 58 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | // See https://code.visualstudio.com/docs/cpp/launch-json-reference 9 | "type": "lldb", 10 | "request": "launch", 11 | "name": "Debug", 12 | "program": "${workspaceFolder}/bin/barco", 13 | "args": [ 14 | "-u", 15 | "0", 16 | "-m", 17 | "/", 18 | "-c", 19 | "/bin/bash", 20 | "-a", 21 | ".", 22 | ], 23 | "cwd": "${workspaceFolder}", 24 | "preLaunchTask": "build debug", 25 | "sourceLanguages": [ 26 | "c" 27 | ] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build debug", 8 | "type": "shell", 9 | "command": "make debug=1", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Luca Cavallin 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Project Settings 2 | debug ?= 0 3 | SRC_DIR := ./src 4 | BUILD_DIR := ./build 5 | INCLUDE_DIR := ./include 6 | LIB_DIR := ./lib 7 | TESTS_DIR := ./tests 8 | BIN_DIR := ./bin 9 | 10 | # Executable settings 11 | BARCO := barco 12 | BARCO_ARGS_0 := --help 13 | BARCO_ARGS_1 := -u 0 -m / -c "/bin/bash -c" -a exit 14 | BARCO_ARGS_2 := -u 0 -m / -c "/bin/bash -c" -a exit -v 15 | 16 | # Libraries settings 17 | LIB_ARGTABLE_REPO := https://github.com/argtable/argtable3/releases/download/v3.2.2.f25c624/argtable-v3.2.2.f25c624-amalgamation.tar.gz 18 | LIB_ARGTABLE_NAME := argtable3 19 | LIB_ARGTABLE_DIR := $(LIB_DIR)/argtable 20 | LIB_ARGTABLE_SRC := $(LIB_ARGTABLE_DIR)/argtable3.c 21 | LIB_LOG_REPO := https://github.com/rxi/log.c/archive/refs/heads/master.zip 22 | LIB_LOG_NAME := log 23 | LIB_LOG_DIR := $(LIB_DIR)/log 24 | LIB_LOG_SRC := $(LIB_LOG_DIR)/log.c 25 | LIB_LOG_FLAGS := -DLOG_USE_COLOR 26 | 27 | # Barco object files 28 | OBJS := $(BARCO).o cgroups.o container.o mount.o sec.o user.o $(LIB_ARGTABLE_NAME).o $(LIB_LOG_NAME).o 29 | 30 | # Compiler settings 31 | CC := clang-18 32 | LINTER := clang-tidy-18 33 | FORMATTER := clang-format-18 34 | DEBUGGER := lldb-18 35 | DISASSEMBLER := llvm-objdump-18 36 | 37 | # Compiler and Linker flags Settings: 38 | # -std=gnu17: Use the GNU17 standard 39 | # -D _GNU_SOURCE: Use GNU extensions 40 | # -D __STDC_WANT_LIB_EXT1__: Use C11 extensions 41 | # -Wall: Enable all warnings 42 | # -Wextra: Enable extra warnings 43 | # -pedantic: Enable pedantic warnings 44 | # -I$(INCLUDE_DIR): Include the include directory 45 | # -I$(LIB_ARGTABLE_DIR): Include the argtable library directory 46 | # -I$(LIB_LOG_DIR): Include the log library directory 47 | # -lcap: Link to libcap 48 | # -lseccomp: Link to libseccomp 49 | # -lm: Link to libm 50 | CFLAGS := -std=gnu17 -D _GNU_SOURCE -D __STDC_WANT_LIB_EXT1__ -Wall -Wextra -pedantic -I$(INCLUDE_DIR) -I$(LIB_ARGTABLE_DIR) -I$(LIB_LOG_DIR) 51 | LFLAGS := -lcap -lseccomp -lm 52 | 53 | ifeq ($(debug), 1) 54 | CFLAGS := $(CFLAGS) -g -O0 55 | else 56 | CFLAGS := $(CFLAGS) -Oz 57 | endif 58 | 59 | # Targets 60 | 61 | # Build barco executable 62 | $(BARCO): format lint dir $(OBJS) 63 | $(CC) $(CFLAGS) $(LFLAGS) -o $(BIN_DIR)/$(BARCO) $(foreach file,$(OBJS),$(BUILD_DIR)/$(file)) 64 | 65 | # Build object files 66 | %.o: dir $(SRC_DIR)/%.c 67 | @$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c 68 | # Build third-party libraries 69 | $(LIB_ARGTABLE_NAME).o: dir $(LIB_ARGTABLE_SRC) 70 | @$(CC) $(CFLAGS) -o $(BUILD_DIR)/$(LIB_ARGTABLE_NAME).o -c $(LIB_ARGTABLE_SRC) 71 | $(LIB_LOG_NAME).o: dir $(LIB_LOG_SRC) 72 | @$(CC) $(CFLAGS) -o $(BUILD_DIR)/$(LIB_LOG_NAME).o -c $(LIB_LOG_SRC) $(LIB_LOG_FLAGS) 73 | 74 | # Run CUnit tests 75 | test: dir 76 | @$(CC) $(CFLAGS) -lcunit -o $(BIN_DIR)/$(BARCO)_test $(TESTS_DIR)/$(BARCO)_test.c 77 | @$(BIN_DIR)/$(BARCO)_test 78 | 79 | # Run linter on source directories 80 | lint: 81 | @$(LINTER) --config-file=.clang-tidy $(SRC_DIR)/* $(INCLUDE_DIR)/* $(TESTS_DIR)/* -- $(CFLAGS) 82 | 83 | # Run formatter on source directories 84 | format: 85 | @$(FORMATTER) -style=file -i $(SRC_DIR)/* $(INCLUDE_DIR)/* $(TESTS_DIR)/* 86 | 87 | # Run valgrind memory checker on executable 88 | check: $(BARCO) 89 | @sudo valgrind -s --leak-check=full --show-leak-kinds=all $(BIN_DIR)/$(BARCO) $(BARCO_ARGS_0) 90 | @sudo valgrind -s --leak-check=full --show-leak-kinds=all $(BIN_DIR)/$(BARCO) $(BARCO_ARGS_1) 91 | @sudo valgrind -s --leak-check=full --show-leak-kinds=all $(BIN_DIR)/$(BARCO) $(BARCO_ARGS_2) 92 | 93 | # Setup dependencies for build and development 94 | setup: 95 | # Update apt and upgrade packages 96 | @sudo apt update 97 | @sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y 98 | 99 | # Install OS dependencies 100 | @sudo apt install -y bash libarchive-tools lsb-release wget software-properties-common gnupg 101 | 102 | # Install LLVM tools required for building the project 103 | @wget https://apt.llvm.org/llvm.sh 104 | @chmod +x llvm.sh 105 | @sudo ./llvm.sh 18 106 | @rm llvm.sh 107 | 108 | # Install Clang development tools 109 | @sudo apt install -y clang-format-18 clang-tidy-18 clang-tools clangd valgrind 110 | 111 | # Install non-standard system libraries 112 | @sudo apt install -y libseccomp-dev libcap-dev 113 | 114 | # Install CUnit testing framework 115 | @sudo apt install -y libcunit1 libcunit1-doc libcunit1-dev 116 | 117 | # Install third-party libraries and structure them 118 | @mkdir -p $(LIB_DIR)/argtable $(LIB_DIR)/log 119 | @echo "Installing argtable..." 120 | @wget -qO- $(LIB_ARGTABLE_REPO) | bsdtar -xvf- --strip=1 -C $(LIB_DIR)/argtable *.c *.h 2> /dev/null 121 | @find $(LIB_DIR)/argtable/* -d -type d -exec rm -rf '{}' \; 2> /dev/null 122 | @echo "Installing log..." 123 | @wget -qO- $(LIB_LOG_REPO) | bsdtar -xvf- --strip=2 -C $(LIB_DIR)/log *.c *.h 2> /dev/null 124 | 125 | # Cleanup 126 | @sudo apt autoremove -y 127 | 128 | # Setup build and bin directories 129 | dir: 130 | @mkdir -p $(BUILD_DIR) $(BIN_DIR) 131 | 132 | # Clean build and bin directories 133 | clean: 134 | @rm -rf $(BUILD_DIR) $(BIN_DIR) 135 | 136 | .PHONY: lint format check setup dir clean deps 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # barco 2 | 3 | `barco` is a project I worked on to learn more about Linux containers and the Linux kernel, based on other guides on the internet. 4 | Linux containers are made up by a set of Linux kernel features: 5 | 6 | - `namespaces`: are used to group kernel objects into different sets that can be accessed by specific process trees. There are different types of `namespaces`, for example,the `PID` namespace is used to isolate the process tree, while the `network` namespace is used to isolate the network stack. 7 | - `seccomp`: is used to limit the system calls that a process can make (handled via syscalls) 8 | - `capabilities`: are used to set limits on what uid 0 (root) can do (handled via syscalls) 9 | - `cgroups`: are used to limit the resources (e.g. memory, disk I/O, CPU-tme) that a process can use (handled via cgroupfs) 10 | 11 | ## Usage 12 | 13 | `barco` can be used to run `bin/sh . ` from the `/` directory as `root` (-u 0) with the following command (optional `-v` for verbose output): 14 | 15 | ```bash 16 | $ sudo ./bin/barco -u 0 -m / -c /bin/sh -a . [-v] 17 | 18 | 22:08:41 INFO ./src/barco.c:96: initializing socket pair... 19 | 22:08:41 INFO ./src/barco.c:103: setting socket flags... 20 | 22:08:41 INFO ./src/barco.c:112: initializing container stack... 21 | 22:08:41 INFO ./src/barco.c:120: initializing container... 22 | 22:08:41 INFO ./src/barco.c:131: initializing cgroups... 23 | 22:08:41 INFO ./src/cgroups.c:73: setting memory.max to 1G... 24 | 22:08:41 INFO ./src/cgroups.c:73: setting cpu.weight to 256... 25 | 22:08:41 INFO ./src/cgroups.c:73: setting pids.max to 64... 26 | 22:08:41 INFO ./src/cgroups.c:73: setting cgroup.procs to 1458... 27 | 22:08:41 INFO ./src/barco.c:139: configuring user namespace... 28 | 22:08:41 INFO ./src/barco.c:147: waiting for container to exit... 29 | 22:08:41 INFO ./src/container.c:43: ### BARCONTAINER STARTING - type 'exit' to quit ### 30 | 31 | # ls 32 | bin home lib32 media root sys vmlinuz 33 | boot initrd.img lib64 mnt run tmp vmlinuz.old 34 | dev initrd.img.old libx32 opt sbin usr 35 | etc lib lost+found proc srv var 36 | # echo "i am a container" 37 | i am a container 38 | # exit 39 | 40 | 22:08:55 INFO ./src/barco.c:153: freeing resources... 41 | 22:08:55 INFO ./src/barco.c:168: so long and thanks for all the fish 42 | ``` 43 | 44 | ## Setup 45 | 46 | `barco` requires a number of tools and libraries to be installed to build the project and for development. 47 | 48 | ```bash 49 | # Install all required tooling and dependencies 50 | $ sudo apt install -y make 51 | $ make setup 52 | ``` 53 | 54 | ### Dependencies 55 | 56 | `barco` depends on the following "non-standard" libraries: 57 | 58 | - `libseccomp`: used to set up seccomp filters 59 | - `libcap`: used to set container capabilities 60 | - `libcuni1`: used for testing with CUnit 61 | - [argtable](http://argtable.org/): used to parse command line arguments 62 | - [rxi/log.c](https://github.com/rxi/log.c): used for logging 63 | 64 | `barco` uses a number of LLVM-18-based tools for development, linting, formatting, debugging and Valgrind to check for memory leaks. 65 | 66 | ## Build 67 | 68 | The included `Makefile` provides a few targets to build `barco`. 69 | The variable `debug=1` can be set to run any of the targets in "debug" mode, which builds the project with debug symbols and without optimizations (especially useful for the debugger and valgrind). 70 | 71 | ```bash 72 | # Build barco (executable is in bin/) 73 | # The default target also runs, "make lint" and "make format" to lint and format the code 74 | $ make 75 | 76 | 77 | # Build barco with debug flags 78 | $ make debug=1 79 | ``` 80 | 81 | ## Development 82 | `barco` is developed using [Visual Studio Code](https://code.visualstudio.com/) and [GitHub Codespaces](https://github.com/codespaces). The repository contains all the necessary configuration files to use these tools effectively. 83 | `barco` relies on low-level Linux features, so it must be run on a Linux system. [GitHub Codespaces](https://github.com/codespaces) acts weird at times when tweaking low-level container settings: I found [getutm.app](https://getutm.app) to work well with [Debian](http://debian.org) on my Mac when in doubt. 84 | 85 | The included `Makefile` provides a few targets useful for development: 86 | 87 | ```bash 88 | # Run tests 89 | $ make test 90 | 91 | # Run linter 92 | $ make lint 93 | 94 | # Run formatter 95 | $ make format 96 | 97 | # Run valgrind 98 | $ make check 99 | 100 | # Clean the build 101 | $ make clean 102 | ``` 103 | 104 | Furthermore, the project includes a [Visual Studio Code](https://code.visualstudio.com/) configuration in `.vscode/` that can be used to run the built-in debugger (at this moment it is "disabled" since `barco` should be run as `root` and [CodeLLDB](https://github.com/vadimcn/codelldb) does not have that option). 105 | 106 | ## Structure 107 | 108 | The project is structured as follows: 109 | 110 | ```txt 111 | ├── .devcontainer configuration for GitHub Codespaces 112 | ├── .github configuration GitHub Actions and other GitHub features 113 | ├── .vscode configuration for Visual Studio Code 114 | ├── bin the executable (created by make) 115 | ├── build intermediate build files e.g. *.o (created by make) 116 | ├── docs documentation 117 | ├── include header files 118 | ├── lib third-party libraries 119 | ├── scripts scripts for setup and other tasks 120 | ├── src C source files 121 | │ ├── barco.c (main) Entry point for the CLI 122 | │ └── *.c 123 | ├── tests contains tests 124 | ├── .clang-format configuration for the formatter 125 | ├── .clang-tidy configuration for the linter 126 | ├── .gitignore 127 | ├── LICENSE 128 | ├── Makefile 129 | └── README.md 130 | ``` 131 | 132 | ## Testing and documentation 133 | 134 | At the moment, the project does not contain any automated tests or tools to document the code. 135 | In the future, suitable tools for automated testing and documentation might be added. 136 | 137 | ## Limitations 138 | 139 | 1. `barco` has been tested on Debian 12, running the Linux kernel at version 6.1.0, with user namespaces and cgroupsv2 enabled. 140 | 141 | 2. `barco` does not handle network namespaces, so the container cannot access the network. Networking can roughly be setup as follows: 142 | 143 | - create a new network namespace 144 | - create a virtual ethernet pair 145 | - move one end of the pair to the new network namespace 146 | - assign an IP address to the interface in the new network namespace 147 | - setup routing and NAT 148 | 149 | In C this is usually done via the `rtnetlink` interface. Furthermore, network usage can be limited with the `net_prio` cgroup controller. 150 | 151 | ## Improvements 152 | 153 | - Investigate further, document and refactor: user and mount and cgroup namespaces, syscalls and capabilities 154 | - The functions in `cgroups.c`, `mount.c`, `sec.c`, `user.c` are specific to `barco` and should be made more generic 155 | - CMake and Conan are industry standards, so they should be used eventually instead of Make and the current build system. Unfortunately, CMake and Conan also add a lot of complexity which is not needed at this time. 156 | 157 | ## Credits 158 | 159 | Some of the resources that have been used to develop `barco` are: 160 | 161 | - [Linux kernel documentation](https://www.kernel.org/doc/html/latest/index.html) 162 | - [user_namespaces docs](https://man7.org/linux/man-pages/man7/user_namespaces.7.html) 163 | - [cgroup_namespaces docs](https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html) 164 | - [mount_namespaces docs](https://man7.org/linux/man-pages/man7/mount_namespaces.7.html) 165 | - [Linux containers in 500 lines of code](https://blog.lizzie.io/linux-containers-in-500-loc.html#fn.6) 166 | - [Containers from scratch](https://medium.com/inside-sumup/containers-from-scratch-part-1-b719effd1e0a) 167 | - [The current adoption status of cgroup v2 in containers](https://medium.com/nttlabs/cgroup-v2-596d035be4d7) 168 | - [Docker under the Hood](https://medium.com/devops-dudes/docker-under-the-hood-0-naming-components-and-runtime-9a89cfbbe783) 169 | - [A deep dive into Linux namespaces](https://ifeanyi.co/posts/linux-namespaces-part-1/) 170 | 171 | ## FAQ 172 | 173 | - **Why C?** I haven't written much C since college and nostalgia got me. 174 | - **What does "barco" mean?** It's [Venetian](https://vec.wikipedia.org/wiki/Barco) (my native language) for "hay barrack". 175 | - **Nice logo, did you design it yourself?** Kind of, I asked Midjourney to come up with some designs. 176 | -------------------------------------------------------------------------------- /docs/barco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucavallin/barco/be04c8c5517f60dde93665010b8b5c62428ca0dd/docs/barco.png -------------------------------------------------------------------------------- /include/cgroups.h: -------------------------------------------------------------------------------- 1 | #ifndef CGROUPS_H 2 | #define CGROUPS_H 3 | 4 | #include 5 | 6 | // Used for cgroups limits initialization 7 | #define CGROUPS_MEMORY_MAX "1G" 8 | #define CGROUPS_CPU_WEIGHT "256" 9 | #define CGROUPS_PIDS_MAX "64" 10 | #define CGROUPS_CGROUP_PROCS "cgroup.procs" 11 | enum { CGROUPS_CONTROL_FIELD_SIZE = 256 }; 12 | 13 | // Initializes cgroups for the hostname 14 | int cgroups_init(char *hostname, pid_t pid); 15 | // Cleans up cgroups for the hostname 16 | int cgroups_free(char *hostname); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/container.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTAINER_H 2 | #define CONTAINER_H 3 | 4 | #include 5 | 6 | enum { 7 | // The stack size for the container 8 | CONTAINER_STACK_SIZE = (1024 * 1024), 9 | }; 10 | 11 | // Represents the configuration for a container. 12 | typedef struct { 13 | uid_t uid; 14 | int fd; 15 | char *hostname; 16 | char *cmd; 17 | char *arg; 18 | char *mnt; 19 | } container_config; 20 | 21 | // Initializes the container. 22 | int container_init(container_config *config, char *stack); 23 | 24 | // Waits for the container to exit. 25 | int container_wait(int container_pid); 26 | 27 | // Stops the container. 28 | void container_stop(int container_pid); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/mount.h: -------------------------------------------------------------------------------- 1 | #ifndef MOUNT_H 2 | #define MOUNT_H 3 | 4 | // Set the mount directory for the process 5 | int mount_set(char *mnt); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /include/sec.h: -------------------------------------------------------------------------------- 1 | #ifndef SEC_H 2 | #define SEC_H 3 | 4 | #include 5 | 6 | // Used to represent the result of a seccomp rule. 7 | #define SEC_SCMP_FAIL SCMP_ACT_ERRNO(EPERM) 8 | 9 | // Setup capabilities for the calling process 10 | int sec_set_caps(void); 11 | 12 | // Setup seccomp for the calling process 13 | int sec_set_seccomp(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/user.h: -------------------------------------------------------------------------------- 1 | #ifndef USER_H 2 | #define USER_H 3 | 4 | #include 5 | 6 | enum { 7 | // User namespace settings 8 | USER_NAMESPACE_UID_PARENT_RANGE_START = 0, 9 | USER_NAMESPACE_UID_CHILD_RANGE_START = 10000, 10 | USER_NAMESPACE_UID_CHILD_RANGE_SIZE = 2000, 11 | }; 12 | 13 | // Setup the user namespace for the process 14 | int user_namespace_init(uid_t uid, int fd); 15 | 16 | // Configures the user and group mappings for the namespace 17 | // so that the child process can set its own user and group 18 | int user_namespace_prepare_mappings(pid_t pid, int fd); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /lib/argtable/argtable3.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * argtable3: Declares the main interfaces of the library 3 | * 4 | * This file is part of the argtable3 library. 5 | * 6 | * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann 7 | * 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of STEWART HEITMANN nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, 25 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ******************************************************************************/ 32 | 33 | #ifndef ARGTABLE3 34 | #define ARGTABLE3 35 | 36 | #include /* FILE */ 37 | #include /* struct tm */ 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | #define ARG_REX_ICASE 1 44 | #define ARG_DSTR_SIZE 200 45 | #define ARG_CMD_NAME_LEN 100 46 | #define ARG_CMD_DESCRIPTION_LEN 256 47 | 48 | #ifndef ARG_REPLACE_GETOPT 49 | #define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */ 50 | #endif /* ARG_REPLACE_GETOPT */ 51 | 52 | /* bit masks for arg_hdr.flag */ 53 | enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 }; 54 | 55 | #if defined(_WIN32) 56 | #if defined(argtable3_EXPORTS) 57 | #define ARG_EXTERN __declspec(dllexport) 58 | #elif defined(argtable3_IMPORTS) 59 | #define ARG_EXTERN __declspec(dllimport) 60 | #else 61 | #define ARG_EXTERN 62 | #endif 63 | #else 64 | #define ARG_EXTERN 65 | #endif 66 | 67 | typedef struct _internal_arg_dstr* arg_dstr_t; 68 | typedef void* arg_cmd_itr_t; 69 | 70 | typedef void(arg_resetfn)(void* parent); 71 | typedef int(arg_scanfn)(void* parent, const char* argval); 72 | typedef int(arg_checkfn)(void* parent); 73 | typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname); 74 | typedef void(arg_dstr_freefn)(char* buf); 75 | typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res); 76 | typedef int(arg_comparefn)(const void* k1, const void* k2); 77 | 78 | /* 79 | * The arg_hdr struct defines properties that are common to all arg_xxx structs. 80 | * The argtable library requires each arg_xxx struct to have an arg_hdr 81 | * struct as its first data member. 82 | * The argtable library functions then use this data to identify the 83 | * properties of the command line option, such as its option tags, 84 | * datatype string, and glossary strings, and so on. 85 | * Moreover, the arg_hdr struct contains pointers to custom functions that 86 | * are provided by each arg_xxx struct which perform the tasks of parsing 87 | * that particular arg_xxx arguments, performing post-parse checks, and 88 | * reporting errors. 89 | * These functions are private to the individual arg_xxx source code 90 | * and are the pointer to them are initiliased by that arg_xxx struct's 91 | * constructor function. The user could alter them after construction 92 | * if desired, but the original intention is for them to be set by the 93 | * constructor and left unaltered. 94 | */ 95 | typedef struct arg_hdr { 96 | char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ 97 | const char* shortopts; /* String defining the short options */ 98 | const char* longopts; /* String defiing the long options */ 99 | const char* datatype; /* Description of the argument data type */ 100 | const char* glossary; /* Description of the option as shown by arg_print_glossary function */ 101 | int mincount; /* Minimum number of occurences of this option accepted */ 102 | int maxcount; /* Maximum number of occurences if this option accepted */ 103 | void* parent; /* Pointer to parent arg_xxx struct */ 104 | arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */ 105 | arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */ 106 | arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */ 107 | arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */ 108 | void* priv; /* Pointer to private header data for use by arg_xxx functions */ 109 | } arg_hdr_t; 110 | 111 | typedef struct arg_rem { 112 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 113 | } arg_rem_t; 114 | 115 | typedef struct arg_lit { 116 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 117 | int count; /* Number of matching command line args */ 118 | } arg_lit_t; 119 | 120 | typedef struct arg_int { 121 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 122 | int count; /* Number of matching command line args */ 123 | int* ival; /* Array of parsed argument values */ 124 | } arg_int_t; 125 | 126 | typedef struct arg_dbl { 127 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 128 | int count; /* Number of matching command line args */ 129 | double* dval; /* Array of parsed argument values */ 130 | } arg_dbl_t; 131 | 132 | typedef struct arg_str { 133 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 134 | int count; /* Number of matching command line args */ 135 | const char** sval; /* Array of parsed argument values */ 136 | } arg_str_t; 137 | 138 | typedef struct arg_rex { 139 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 140 | int count; /* Number of matching command line args */ 141 | const char** sval; /* Array of parsed argument values */ 142 | } arg_rex_t; 143 | 144 | typedef struct arg_file { 145 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 146 | int count; /* Number of matching command line args*/ 147 | const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */ 148 | const char** basename; /* Array of parsed basenames (eg: foo.bar) */ 149 | const char** extension; /* Array of parsed extensions (eg: .bar) */ 150 | } arg_file_t; 151 | 152 | typedef struct arg_date { 153 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 154 | const char* format; /* strptime format string used to parse the date */ 155 | int count; /* Number of matching command line args */ 156 | struct tm* tmval; /* Array of parsed time values */ 157 | } arg_date_t; 158 | 159 | enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG }; 160 | typedef struct arg_end { 161 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 162 | int count; /* Number of errors encountered */ 163 | int* error; /* Array of error codes */ 164 | void** parent; /* Array of pointers to offending arg_xxx struct */ 165 | const char** argval; /* Array of pointers to offending argv[] string */ 166 | } arg_end_t; 167 | 168 | typedef struct arg_cmd_info { 169 | char name[ARG_CMD_NAME_LEN]; 170 | char description[ARG_CMD_DESCRIPTION_LEN]; 171 | arg_cmdfn* proc; 172 | } arg_cmd_info_t; 173 | 174 | /**** arg_xxx constructor functions *********************************/ 175 | 176 | ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary); 177 | 178 | ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary); 179 | ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary); 180 | ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary); 181 | 182 | ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 183 | ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 184 | ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); 185 | 186 | ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 187 | ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 188 | ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); 189 | 190 | ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 191 | ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 192 | ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); 193 | 194 | ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); 195 | ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); 196 | ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts, 197 | const char* longopts, 198 | const char* pattern, 199 | const char* datatype, 200 | int mincount, 201 | int maxcount, 202 | int flags, 203 | const char* glossary); 204 | 205 | ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 206 | ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); 207 | ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); 208 | 209 | ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); 210 | ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); 211 | ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary); 212 | 213 | ARG_EXTERN struct arg_end* arg_end(int maxcount); 214 | 215 | #define ARG_DSTR_STATIC ((arg_dstr_freefn*)0) 216 | #define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1) 217 | #define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3) 218 | 219 | /**** other functions *******************************************/ 220 | ARG_EXTERN int arg_nullcheck(void** argtable); 221 | ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable); 222 | ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); 223 | ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix); 224 | ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix); 225 | ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format); 226 | ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable); 227 | ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); 228 | ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); 229 | ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix); 230 | ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix); 231 | ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format); 232 | ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable); 233 | ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname); 234 | ARG_EXTERN void arg_freetable(void** argtable, size_t n); 235 | 236 | ARG_EXTERN arg_dstr_t arg_dstr_create(void); 237 | ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds); 238 | ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds); 239 | ARG_EXTERN void arg_dstr_free(arg_dstr_t ds); 240 | ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc); 241 | ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str); 242 | ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c); 243 | ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...); 244 | ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds); 245 | 246 | ARG_EXTERN void arg_cmd_init(void); 247 | ARG_EXTERN void arg_cmd_uninit(void); 248 | ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description); 249 | ARG_EXTERN void arg_cmd_unregister(const char* name); 250 | ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res); 251 | ARG_EXTERN unsigned int arg_cmd_count(void); 252 | ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name); 253 | ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void); 254 | ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr); 255 | ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr); 256 | ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr); 257 | ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr); 258 | ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k); 259 | ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn); 260 | ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res); 261 | ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable); 262 | ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end); 263 | ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode); 264 | ARG_EXTERN void arg_set_module_name(const char* name); 265 | ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag); 266 | 267 | /**** deprecated functions, for back-compatibility only ********/ 268 | ARG_EXTERN void arg_free(void** argtable); 269 | 270 | #ifdef __cplusplus 271 | } 272 | #endif 273 | #endif 274 | -------------------------------------------------------------------------------- /lib/log/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #define MAX_CALLBACKS 32 26 | 27 | typedef struct { 28 | log_LogFn fn; 29 | void *udata; 30 | int level; 31 | } Callback; 32 | 33 | static struct { 34 | void *udata; 35 | log_LockFn lock; 36 | int level; 37 | bool quiet; 38 | Callback callbacks[MAX_CALLBACKS]; 39 | } L; 40 | 41 | 42 | static const char *level_strings[] = { 43 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 44 | }; 45 | 46 | #ifdef LOG_USE_COLOR 47 | static const char *level_colors[] = { 48 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 49 | }; 50 | #endif 51 | 52 | 53 | static void stdout_callback(log_Event *ev) { 54 | char buf[16]; 55 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; 56 | #ifdef LOG_USE_COLOR 57 | fprintf( 58 | ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 59 | buf, level_colors[ev->level], level_strings[ev->level], 60 | ev->file, ev->line); 61 | #else 62 | fprintf( 63 | ev->udata, "%s %-5s %s:%d: ", 64 | buf, level_strings[ev->level], ev->file, ev->line); 65 | #endif 66 | vfprintf(ev->udata, ev->fmt, ev->ap); 67 | fprintf(ev->udata, "\n"); 68 | fflush(ev->udata); 69 | } 70 | 71 | 72 | static void file_callback(log_Event *ev) { 73 | char buf[64]; 74 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; 75 | fprintf( 76 | ev->udata, "%s %-5s %s:%d: ", 77 | buf, level_strings[ev->level], ev->file, ev->line); 78 | vfprintf(ev->udata, ev->fmt, ev->ap); 79 | fprintf(ev->udata, "\n"); 80 | fflush(ev->udata); 81 | } 82 | 83 | 84 | static void lock(void) { 85 | if (L.lock) { L.lock(true, L.udata); } 86 | } 87 | 88 | 89 | static void unlock(void) { 90 | if (L.lock) { L.lock(false, L.udata); } 91 | } 92 | 93 | 94 | const char* log_level_string(int level) { 95 | return level_strings[level]; 96 | } 97 | 98 | 99 | void log_set_lock(log_LockFn fn, void *udata) { 100 | L.lock = fn; 101 | L.udata = udata; 102 | } 103 | 104 | 105 | void log_set_level(int level) { 106 | L.level = level; 107 | } 108 | 109 | 110 | void log_set_quiet(bool enable) { 111 | L.quiet = enable; 112 | } 113 | 114 | 115 | int log_add_callback(log_LogFn fn, void *udata, int level) { 116 | for (int i = 0; i < MAX_CALLBACKS; i++) { 117 | if (!L.callbacks[i].fn) { 118 | L.callbacks[i] = (Callback) { fn, udata, level }; 119 | return 0; 120 | } 121 | } 122 | return -1; 123 | } 124 | 125 | 126 | int log_add_fp(FILE *fp, int level) { 127 | return log_add_callback(file_callback, fp, level); 128 | } 129 | 130 | 131 | static void init_event(log_Event *ev, void *udata) { 132 | if (!ev->time) { 133 | time_t t = time(NULL); 134 | ev->time = localtime(&t); 135 | } 136 | ev->udata = udata; 137 | } 138 | 139 | 140 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 141 | log_Event ev = { 142 | .fmt = fmt, 143 | .file = file, 144 | .line = line, 145 | .level = level, 146 | }; 147 | 148 | lock(); 149 | 150 | if (!L.quiet && level >= L.level) { 151 | init_event(&ev, stderr); 152 | va_start(ev.ap, fmt); 153 | stdout_callback(&ev); 154 | va_end(ev.ap); 155 | } 156 | 157 | for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { 158 | Callback *cb = &L.callbacks[i]; 159 | if (level >= cb->level) { 160 | init_event(&ev, cb->udata); 161 | va_start(ev.ap, fmt); 162 | cb->fn(&ev); 163 | va_end(ev.ap); 164 | } 165 | } 166 | 167 | unlock(); 168 | } 169 | -------------------------------------------------------------------------------- /lib/log/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | typedef struct { 19 | va_list ap; 20 | const char *fmt; 21 | const char *file; 22 | struct tm *time; 23 | void *udata; 24 | int line; 25 | int level; 26 | } log_Event; 27 | 28 | typedef void (*log_LogFn)(log_Event *ev); 29 | typedef void (*log_LockFn)(bool lock, void *udata); 30 | 31 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 32 | 33 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 34 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 35 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 36 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 37 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 38 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 39 | 40 | const char* log_level_string(int level); 41 | void log_set_lock(log_LockFn fn, void *udata); 42 | void log_set_level(int level); 43 | void log_set_quiet(bool enable); 44 | int log_add_callback(log_LogFn fn, void *udata, int level); 45 | int log_add_fp(FILE *fp, int level); 46 | 47 | void log_log(int level, const char *file, int line, const char *fmt, ...); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/barco.c: -------------------------------------------------------------------------------- 1 | #include "argtable3.h" 2 | #include "cgroups.h" 3 | #include "container.h" 4 | #include "log.h" 5 | #include "user.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | enum { 13 | // ARGTABLE_ARG_MAX is the maximum number of arguments 14 | ARGTABLE_ARG_MAX = 20 15 | }; 16 | 17 | /* global arg_xxx structs */ 18 | struct arg_lit *help, *version; 19 | struct arg_int *uid; 20 | struct arg_str *mnt; 21 | struct arg_str *cmd; 22 | struct arg_str *arg; 23 | struct arg_lit *vrb; 24 | struct arg_end *end; 25 | 26 | int main(int argc, char **argv) { 27 | // used for container stack 28 | char *stack = 0; 29 | // used for container config 30 | container_config config = {0}; 31 | // used for container pid 32 | int container_pid = 0; 33 | // socket pair used for communication between barco and container 34 | int sockets[2] = {0}; 35 | 36 | // the global arg_xxx structs are initialised within the argtable 37 | void *argtable[] = { 38 | help = arg_litn(NULL, "help", 0, 1, "display this help and exit"), 39 | version = 40 | arg_litn(NULL, "version", 0, 1, "display version info and exit"), 41 | uid = arg_intn("u", "uid", "", 1, 1, 42 | "uid and gid of the user in the container"), 43 | mnt = arg_strn("m", "mnt", "", 1, 1, 44 | "directory to mount as root in the container"), 45 | cmd = 46 | arg_strn("c", "cmd", "", 1, 1, "command to run in the container"), 47 | arg = 48 | arg_strn("a", "arg", "", 0, 1, "argument to pass to the command"), 49 | vrb = arg_litn("v", "verbosity", 0, 1, "verbose output"), 50 | end = arg_end(ARGTABLE_ARG_MAX), 51 | }; 52 | 53 | int exitcode = 0; 54 | char progname[] = "barco"; 55 | 56 | int nerrors; 57 | nerrors = arg_parse(argc, argv, argtable); 58 | 59 | // special case: '--help' takes precedence over error reporting 60 | if (help->count > 0) { 61 | printf("Usage: %s", progname); 62 | arg_print_syntax(stdout, argtable, "\n"); 63 | arg_print_glossary(stdout, argtable, " %-25s %s\n"); 64 | exitcode = 0; 65 | goto exit; 66 | } 67 | 68 | // If the parser returned any errors then display them and exit 69 | if (nerrors > 0) { 70 | // Display the error details contained in the arg_end struct. 71 | arg_print_errors(stdout, end, progname); 72 | printf("Try '%s --help' for more information.\n", progname); 73 | exitcode = 1; 74 | goto exit; 75 | } 76 | 77 | // Set verbosity level 78 | log_set_level(LOG_INFO); 79 | if (vrb->count > 0) { 80 | log_set_level(LOG_TRACE); 81 | } 82 | 83 | config.cmd = (char *)cmd->sval[0]; 84 | config.arg = (char *)arg->sval[0]; 85 | config.mnt = (char *)mnt->sval[0]; 86 | 87 | // Check if barco is running as root 88 | if (geteuid() != 0) { 89 | log_warn("barco should be run as root"); 90 | } 91 | 92 | // Set hostname for the container to "barcontainer" 93 | config.hostname = "barcontainer"; 94 | 95 | // Initialize a socket pair to communicate with the container 96 | log_info("initializing socket pair..."); 97 | if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, sockets)) { 98 | log_fatal("failed to initialialize socket pair: %m"); 99 | exitcode = 1; 100 | goto exit; 101 | } 102 | 103 | log_info("setting socket flags..."); 104 | if (fcntl(sockets[0], F_SETFD, FD_CLOEXEC)) { 105 | log_fatal("failed to socket fcntl: %m"); 106 | exitcode = 1; 107 | goto exit; 108 | } 109 | config.fd = sockets[1]; 110 | 111 | // Initialize a stack for the container 112 | log_info("initializing container stack..."); 113 | if (!(stack = malloc(CONTAINER_STACK_SIZE))) { 114 | log_fatal("failed to initialize container stack: %m"); 115 | exitcode = 1; 116 | goto cleanup; 117 | } 118 | 119 | // Initialize the container (calls clone() internally). 120 | log_info("initializing container..."); 121 | // Stacks on most architectures grow downwards. 122 | // CONTAINER_STACK_SIZE gives us a pointer just below the end. 123 | if ((container_pid = container_init(&config, stack + CONTAINER_STACK_SIZE)) == 124 | -1) { 125 | log_fatal("failed to container_init"); 126 | exitcode = 1; 127 | goto cleanup; 128 | } 129 | 130 | // Prepare cgroups for the process (the container is a child process of barco) 131 | log_info("initializing cgroups..."); 132 | if (cgroups_init(config.hostname, container_pid)) { 133 | log_fatal("failed to initialize cgroups"); 134 | exitcode = 1; 135 | goto cleanup; 136 | } 137 | 138 | // Barco configures the user namespace for the container 139 | log_info("configuring user namespace..."); 140 | if (user_namespace_prepare_mappings(container_pid, sockets[0])) { 141 | exitcode = 1; 142 | log_fatal("failed to user_namespace_set_user, stopping container..."); 143 | goto cleanup; 144 | } 145 | 146 | // Wait for the container to exit 147 | log_info("waiting for container to exit..."); 148 | exitcode |= container_wait(container_pid); 149 | log_debug("container exited..."); 150 | 151 | cleanup: 152 | // Clear resources (cgroups, stack, sockets) 153 | log_info("freeing resources..."); 154 | 155 | log_debug("freeing stack..."); 156 | free(stack); 157 | 158 | log_debug("freeing sockets..."); 159 | close(sockets[0]); 160 | close(sockets[1]); 161 | 162 | log_debug("freeing cgroups..."); 163 | cgroups_free(config.hostname); 164 | 165 | exit: 166 | log_debug("freeing argtable..."); 167 | arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); 168 | log_info("so long and thanks for all the fish"); 169 | return exitcode; 170 | } 171 | -------------------------------------------------------------------------------- /src/cgroups.c: -------------------------------------------------------------------------------- 1 | #include "cgroups.h" 2 | #include "log.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // This struct is used to store cgroups settings. 12 | struct cgroups_setting { 13 | char name[CGROUPS_CONTROL_FIELD_SIZE]; 14 | char value[CGROUPS_CONTROL_FIELD_SIZE]; 15 | }; 16 | 17 | // cgroups settings are written to the cgroups v2 filesystem as follows: 18 | // - create a directory for the new cgroup 19 | // - settings files are created automatically 20 | // - write the settings to the corresponding files 21 | int cgroups_init(char *hostname, pid_t pid) { 22 | char cgroup_dir[PATH_MAX] = {0}; 23 | 24 | // The "cgroup.procs" setting is used to add a process to a cgroup. 25 | // It is prepared here with the pid of the calling process, so that it can be 26 | // added to the cgroup later. 27 | struct cgroups_setting *procs_setting = 28 | &(struct cgroups_setting){.name = CGROUPS_CGROUP_PROCS, .value = ""}; 29 | ; 30 | if (snprintf(procs_setting->value, CGROUPS_CONTROL_FIELD_SIZE, "%d", pid) == 31 | -1) { 32 | log_error("failed to setup cgroup.procs setting: %m"); 33 | return -1; 34 | } 35 | // Cgroups let us limit resources allocated to a process to prevent it from 36 | // dying services to the rest of the system. The cgroups must be created 37 | // before the process enters a cgroups namespace. The following settings are 38 | // applied: 39 | // - memory.limit_in_bytes: 1GB (process memory limit) 40 | // - cpu.shares: 256 (a quarter of the CPU time) 41 | // - pids.max: 64 (max number of processes) 42 | // - cgroup.procs: 0 (the calling process is added to the cgroup) 43 | struct cgroups_setting *cgroups_setting_list[] = { 44 | &(struct cgroups_setting){.name = "memory.max", 45 | .value = CGROUPS_MEMORY_MAX}, 46 | &(struct cgroups_setting){.name = "cpu.weight", 47 | .value = CGROUPS_CPU_WEIGHT}, 48 | &(struct cgroups_setting){.name = "pids.max", .value = CGROUPS_PIDS_MAX}, 49 | procs_setting, NULL}; 50 | 51 | log_debug("setting cgroups..."); 52 | 53 | // Create the cgroup directory. 54 | if (snprintf(cgroup_dir, sizeof(cgroup_dir), "/sys/fs/cgroup/%s", hostname) == 55 | -1) { 56 | log_error("failed to setup path: %m"); 57 | return -1; 58 | } 59 | 60 | log_debug("creating %s...", cgroup_dir); 61 | if (mkdir(cgroup_dir, S_IRUSR | S_IWUSR | S_IXUSR)) { 62 | log_error("failed to mkdir %s: %m", cgroup_dir); 63 | return -1; 64 | } 65 | 66 | // Loop through and write settings to the corresponding files in the cgroup 67 | // directory. 68 | for (struct cgroups_setting **setting = cgroups_setting_list; *setting; 69 | setting++) { 70 | char setting_dir[PATH_MAX] = {0}; 71 | int fd = 0; 72 | 73 | log_info("setting %s to %s...", (*setting)->name, (*setting)->value); 74 | if (snprintf(setting_dir, sizeof(setting_dir), "%s/%s", cgroup_dir, 75 | (*setting)->name) == -1) { 76 | log_error("failed to setup path: %m"); 77 | return -1; 78 | } 79 | 80 | log_debug("opening %s...", setting_dir); 81 | if ((fd = open(setting_dir, O_WRONLY)) == -1) { 82 | log_error("failed to open %s: %m", setting_dir); 83 | return -1; 84 | } 85 | 86 | log_debug("writing %s to setting", (*setting)->value); 87 | if (write(fd, (*setting)->value, strlen((*setting)->value)) == -1) { 88 | log_error("failed to write %s: %m", setting_dir); 89 | close(fd); 90 | return -1; 91 | } 92 | 93 | log_debug("closing %s...", setting_dir); 94 | if (close(fd)) { 95 | log_error("failed to close %s: %m", setting_dir); 96 | return -1; 97 | } 98 | } 99 | 100 | log_debug("cgroups set"); 101 | return 0; 102 | } 103 | 104 | // Clean up the cgroups for the process. Since barco write the PID of its child 105 | // process to the cgroup.procs file, all that is needed is to remove the cgroups 106 | // directory after the child process is exited. 107 | int cgroups_free(char *hostname) { 108 | char dir[PATH_MAX] = {0}; 109 | 110 | log_debug("freeing cgroups..."); 111 | 112 | if (snprintf(dir, sizeof(dir), "/sys/fs/cgroup/%s", hostname) == -1) { 113 | log_error("failed to setup paths: %m"); 114 | return -1; 115 | } 116 | 117 | log_debug("removing %s...", dir); 118 | if (rmdir(dir)) { 119 | log_error("failed to rmdir %s: %m", dir); 120 | return -1; 121 | } 122 | 123 | log_debug("cgroups released"); 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/container.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 1 2 | #include "container.h" 3 | #include "log.h" 4 | #include "mount.h" 5 | #include "sec.h" 6 | #include "user.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // This is the function that will be called by clone() to start the container. 17 | // The order of the operations is of important as, for example, 18 | // mounts cannot be changed without specific capabilities, 19 | // unshare cannot be called after syscalls are limited, etc... 20 | int container_start(void *arg) { 21 | container_config *config = arg; 22 | 23 | log_debug("starting container"); 24 | log_debug( 25 | "setting hostname, mounts, user namespace, capabilities and syscalls..."); 26 | 27 | if (sethostname(config->hostname, strlen(config->hostname)) || 28 | mount_set(config->mnt) || user_namespace_init(config->uid, config->fd) || 29 | sec_set_caps() || sec_set_seccomp()) { 30 | log_debug("failed to set properties"); 31 | close(config->fd); 32 | return -1; 33 | } 34 | 35 | log_debug("closing container socket..."); 36 | if (close(config->fd)) { 37 | log_error("failed to close container socket: %m"); 38 | return -1; 39 | } 40 | 41 | log_debug("executing command '%s %s' from directory '%s' in container...", 42 | config->cmd, config->arg, config->mnt); 43 | log_info("### BARCONTAINER STARTING - type 'exit' to quit ###"); 44 | // argv must be NULL terminated 45 | char *argv[] = {config->arg, NULL}; 46 | if (execve(config->cmd, argv, NULL)) { 47 | log_error("failed to execve '%s %s': %m", config->cmd, argv); 48 | return -1; 49 | } 50 | log_debug("container started..."); 51 | 52 | return 0; 53 | } 54 | 55 | // Creates container (process) with different properties than its parent 56 | // e.g. mount to different dir, different hostname, etc... 57 | // All these requirements are specified by the flags we pass to clone() 58 | int container_init(container_config *config, char *stack) { 59 | int container_pid = 0; 60 | // The flags specify what the cloned process can do. 61 | // These allow some control overrmounts, pids, IPC data structures, network 62 | // devices and hostname. 63 | int flags = CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWPID | CLONE_NEWIPC | 64 | CLONE_NEWNET | CLONE_NEWUTS; 65 | 66 | // SIGCHLD lets us wait on the child process. 67 | log_debug("cloning process..."); 68 | if ((container_pid = 69 | clone(container_start, stack, flags | SIGCHLD, config)) == -1) { 70 | log_error("failed to clone: %m"); 71 | return 1; 72 | } 73 | 74 | // Close and zero the child's socket, to avoid leaving an open fd in 75 | // case of a failure. If we do not do this, the child or parent might hang. 76 | log_debug("resetting container socket..."); 77 | close(config->fd); 78 | config->fd = 0; 79 | 80 | return container_pid; 81 | } 82 | 83 | int container_wait(int container_pid) { 84 | int container_status = 0; 85 | 86 | log_debug("waiting for container_pid %d...", container_pid); 87 | waitpid(container_pid, &container_status, 0); 88 | log_debug("container_pid %d exited", container_pid); 89 | 90 | return WEXITSTATUS(container_status); 91 | } 92 | 93 | void container_stop(int container_pid) { 94 | log_debug("calling kill for container_pid %d...", container_pid); 95 | if (kill(container_pid, SIGKILL)) { 96 | log_error("failed to kill container_pid %d: %m", container_pid); 97 | } 98 | log_debug("container_pid %d killed", container_pid); 99 | } 100 | -------------------------------------------------------------------------------- /src/mount.c: -------------------------------------------------------------------------------- 1 | #include "mount.h" 2 | #include "log.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // pivot_root is a system call to swap the mount at / with another. 12 | // glibc does not provide a wrapper for it. 13 | long pivot_root(const char *new_root, const char *put_old) { 14 | log_debug("calling pivot_root syscall..."); 15 | return syscall(SYS_pivot_root, new_root, put_old); 16 | } 17 | 18 | // Restricts access to resources the process has in its own mount namespace: 19 | // - Create a temporary directory and one inside of it 20 | // - Bind mount of the user argument onto the temporary directory 21 | // - pivot_root makes the bind mount the new root and mounts the old root onto 22 | // the inner temporary directory 23 | // - umount the old root and remove the inner temporary directory. 24 | int mount_set(char *mnt) { 25 | log_debug("setting mount..."); 26 | 27 | // MS_PRIVATE makes the bind mount invisible outside of the namespace 28 | // MS_REC makes the mount recursive 29 | log_debug("remounting with MS_PRIVATE..."); 30 | if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { 31 | log_error("failed to mount /: %m"); 32 | return -1; 33 | } 34 | log_debug("remounted"); 35 | 36 | log_debug("creating temporary directory and..."); 37 | char mount_dir[] = "/tmp/barco.XXXXXX"; 38 | if (!mkdtemp(mount_dir)) { 39 | log_error("failed to create directory %s: %m", mount_dir); 40 | return -1; 41 | } 42 | 43 | log_debug("bind mount..."); 44 | if (mount(mnt, mount_dir, NULL, MS_BIND | MS_PRIVATE, NULL)) { 45 | log_error("failed to bind mount on %s: %m", mnt); 46 | return -1; 47 | } 48 | 49 | log_debug("creating inner directory..."); 50 | char inner_mount_dir[] = "/tmp/barco.XXXXXX/oldroot.XXXXXX"; 51 | memcpy(inner_mount_dir, mount_dir, sizeof(mount_dir) - 1); 52 | if (!mkdtemp(inner_mount_dir)) { 53 | log_error("failed to create inner directory %s: %m", inner_mount_dir); 54 | return -1; 55 | } 56 | 57 | log_debug("pivot root with %s, %s...", mount_dir, inner_mount_dir); 58 | if (pivot_root(mount_dir, inner_mount_dir)) { 59 | log_error("failed to pivot root with %s, %s: %m", mount_dir, 60 | inner_mount_dir); 61 | return -1; 62 | } 63 | 64 | log_debug("unmounting old root..."); 65 | char *old_root_dir = basename(inner_mount_dir); 66 | char old_root[sizeof(inner_mount_dir) + 1] = {"/"}; 67 | char *end = memccpy(&old_root[1], old_root_dir, '\0', sizeof old_root - 1); 68 | assert(end != NULL); // truncation shouldn't occur 69 | 70 | log_debug("changing directory to /..."); 71 | if (chdir("/")) { 72 | log_error("failed to chdir to /: %m"); 73 | return -1; 74 | } 75 | 76 | log_debug("unmounting..."); 77 | if (umount2(old_root, MNT_DETACH)) { 78 | log_error("failed to umount %s: %m", old_root); 79 | return -1; 80 | } 81 | 82 | log_debug("removing temporary directories..."); 83 | if (rmdir(old_root)) { 84 | log_error("failed to rmdir %s: %m", old_root); 85 | return -1; 86 | } 87 | 88 | log_debug("mount set"); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /src/sec.c: -------------------------------------------------------------------------------- 1 | #include "sec.h" 2 | #include "log.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Capabilities are used to finely define the privileges of a process. 14 | // The process' own inheritable set and bounding set of capabilities 15 | // should be dropped before setting them to the desired values. 16 | // 17 | // The following capabilities are dropped: 18 | // CAP_AUDIT_CONTROL, _READ, and _WRITE: allow access to the kernel audit system 19 | // CAP_BLOCK_SUSPEND: allow preventing system suspend 20 | // CAP_DAC_READ_SEARCH: allow bypassing file/dir permission checks 21 | // CAP_FSETID: allow setting arbitrary process IDs 22 | // CAP_IPC_LOCK: allow locking memory 23 | // CAP_MAC_ADMIN and _OVERRIDE: allow MAC configuration or state changes 24 | // CAP_MKNOD: allow creating special files using mknod() 25 | // CAP_SETFCAP: allow setting arbitrary capabilities 26 | // CAP_SYSLOG: allow reading and writing kernel logs 27 | // CAP_SYS_ADMIN: allow configuring the system 28 | // CAP_SYS_BOOT: allow rebooting the system 29 | // CAP_SYS_MODULE: allow loading and unloading kernel modules 30 | // CAP_SYS_NICE: allow raising process priority 31 | // CAP_SYS_RAWIO: allow raw I/O operations 32 | // CAP_SYS_RESOURCE: allow configuring resource limits 33 | // CAP_SYS_TIME: allow setting system time 34 | // CAP_WAKE_ALARM: allow waking the system from suspend 35 | // 36 | // The following capabilities are retained: 37 | // CAP_DAC_OVERRIDE: allow bypassing file/dir permission checks inside the 38 | // mount namespace 39 | // CAP_FOWNER, CAP_LEASE, and CAP_LINUX_IMMUTABLE: allow file/dir actions inside 40 | // the mount namespace 41 | // CAP_SYS_PACCT: allow configuring process accounting 42 | // CAP_IPC_OWNER: allow IPC object configuration inside namespace 43 | // CAP_NET_ADMIN: allow configuring network interfaces 44 | // CAP_NET_BIND_SERVICE: allow binding to ports below 1024 45 | // CAP_NET_RAW: allow raw socket operations 46 | // CAP_SYS_PTRACE: allow ptrace(), but not across pid namespaces 47 | // CAP_KILL: allow sending signals to processes, but not across pid namespaces 48 | // CAP_SETUID and CAPSETGID: allow setting arbitrary uids/gids 49 | // CAP_SETPCAP: allow setting arbitrary, already-assigned capabilities 50 | // CAP_SYS_CHROOT: allow chroot() (has risks) 51 | // CAP_SYS_TTYCONFIG: allow configuring TTY devices (has risks) 52 | // 53 | // Notice: in some edge cases, some capabilities might not be respected because 54 | // they are not namespaced (e.g. when writing to parts of procfs) 55 | int sec_set_caps(void) { 56 | log_debug("setting capabilities..."); 57 | int drop_caps[] = { 58 | CAP_AUDIT_CONTROL, CAP_AUDIT_READ, CAP_AUDIT_WRITE, CAP_BLOCK_SUSPEND, 59 | CAP_DAC_READ_SEARCH, CAP_FSETID, CAP_IPC_LOCK, CAP_MAC_ADMIN, 60 | CAP_MAC_OVERRIDE, CAP_MKNOD, CAP_SETFCAP, CAP_SYSLOG, 61 | CAP_SYS_ADMIN, CAP_SYS_BOOT, CAP_SYS_MODULE, CAP_SYS_NICE, 62 | CAP_SYS_RAWIO, CAP_SYS_RESOURCE, CAP_SYS_TIME, CAP_WAKE_ALARM}; 63 | 64 | int num_caps = sizeof(drop_caps) / sizeof(*drop_caps); 65 | log_debug("dropping bounding capabilities..."); 66 | for (int i = 0; i < num_caps; i++) { 67 | if (prctl(PR_CAPBSET_DROP, drop_caps[i], 0, 0, 0)) { 68 | log_error("failed to prctl cap %d: %m", drop_caps[i]); 69 | return 1; 70 | } 71 | } 72 | 73 | log_debug("dropping inheritable capabilities..."); 74 | cap_t caps = NULL; 75 | if (!(caps = cap_get_proc()) || 76 | cap_set_flag(caps, CAP_INHERITABLE, num_caps, drop_caps, CAP_CLEAR) || 77 | cap_set_proc(caps)) { 78 | log_error("failed to run cap functions: %m"); 79 | if (caps) { 80 | log_debug("freeing caps..."); 81 | cap_free(caps); 82 | } 83 | 84 | return 1; 85 | } 86 | 87 | log_debug("freeing caps..."); 88 | cap_free(caps); 89 | log_debug("capabilities set"); 90 | 91 | return 0; 92 | } 93 | 94 | // Blocks sensitive system calls based on Docker's default seccomp profile 95 | // and other obsolete or dangerous system calls. 96 | // The syscalls disallowed by the default Docker policy but permitted by this 97 | // code are: 98 | // - get_mempolicy, getpagesize, pciconfig_iobase, ustat, sysfs: These 99 | // reveal information abour memory layout, PCI devices, filesystem 100 | // - uselib: allows loading a shared library in userspace 101 | // 102 | // Other syscalls are (either): 103 | // - already prevented by the capabilities set 104 | // - available only on particular architectures 105 | // - newer versions or aliases of other syscalls 106 | // 107 | // New Linux syscalls are added to the kernel over time, so this list 108 | // should be updated periodically. 109 | int sec_set_seccomp(void) { 110 | scmp_filter_ctx ctx = NULL; 111 | 112 | log_debug("setting syscalls..."); 113 | if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)) || 114 | // Calls that allow creating new setuid / setgid executables. 115 | // The contained process could created a setuid binary that can be used 116 | // by an user to get root in absence of user namespaces. 117 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(chmod), 1, 118 | SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) || 119 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(chmod), 1, 120 | SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) || 121 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(fchmod), 1, 122 | SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) || 123 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(fchmod), 1, 124 | SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) || 125 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(fchmodat), 1, 126 | SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) || 127 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(fchmodat), 1, 128 | SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) || 129 | 130 | // Calls that allow contained processes to start new user namespaces 131 | // and possibly allow processes to gain new capabilities. 132 | seccomp_rule_add( 133 | ctx, SEC_SCMP_FAIL, SCMP_SYS(unshare), 1, 134 | SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) || 135 | seccomp_rule_add( 136 | ctx, SEC_SCMP_FAIL, SCMP_SYS(clone), 1, 137 | SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) || 138 | 139 | // Allows contained processes to write to the controlling terminal 140 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(ioctl), 1, 141 | SCMP_A1(SCMP_CMP_MASKED_EQ, TIOCSTI, TIOCSTI)) || 142 | 143 | // The kernel keyring system is not namespaced 144 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(keyctl), 0) || 145 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(add_key), 0) || 146 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(request_key), 0) || 147 | 148 | // Before Linux 4.8, ptrace breaks seccomp 149 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(ptrace), 0) || 150 | 151 | // Calls that let processes assign NUMA nodes. These could be used to deny 152 | // service to other NUMA-aware application on the host. 153 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(mbind), 0) || 154 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(migrate_pages), 0) || 155 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(move_pages), 0) || 156 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(set_mempolicy), 0) || 157 | 158 | // Alows userspace to handle page faults It can be used to pause execution 159 | // in the kernel by triggering page faults in system calls, a mechanism 160 | // often used in kernel exploits. 161 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(userfaultfd), 0) || 162 | 163 | // This call could leak a lot of information on the host. 164 | // It can theoretically be used to discover kernel addresses and 165 | // uninitialized memory. 166 | seccomp_rule_add(ctx, SEC_SCMP_FAIL, SCMP_SYS(perf_event_open), 0) || 167 | 168 | // Prevents setuid and setcap'd binaries from being executed 169 | // with additional privileges. This has some security benefits, but due to 170 | // weird side-effects, the ping command will not work in a process for 171 | // an unprivileged user. 172 | seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0) || seccomp_load(ctx)) { 173 | 174 | log_error("failed to set syscalls: %m"); 175 | 176 | // Apply restrictions to the process and release the context. 177 | log_debug("releasing seccomp context..."); 178 | if (ctx) { 179 | seccomp_release(ctx); 180 | } 181 | 182 | return 1; 183 | } 184 | 185 | log_debug("releasing seccomp context..."); 186 | seccomp_release(ctx); 187 | log_debug("syscalls set"); 188 | 189 | return 0; 190 | } 191 | -------------------------------------------------------------------------------- /src/user.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 1 2 | #include "user.h" 3 | #include "log.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Lets the parent process know that the user namespace is started. 13 | // The parent calls user_namespace_set_user to update the uid_map / gid_map. 14 | // If successful, setgroups, setresgid, and setresuid are called in this 15 | // function by the child. setgroups and setresgid are necessary because of two 16 | // separate group mechanisms on Linux. The function assumes that every uid has a 17 | // corresponding gid, which is often the case. 18 | int user_namespace_init(uid_t uid, int fd) { 19 | int unshared = unshare(CLONE_NEWUSER); 20 | int result = 0; 21 | 22 | log_debug("setting user namespace..."); 23 | 24 | log_debug("writing to socket..."); 25 | if (write(fd, &unshared, sizeof(unshared)) != sizeof(unshared)) { 26 | log_error("failed to write socket %d: %m", fd); 27 | return -1; 28 | } 29 | 30 | log_debug("reading from socket..."); 31 | if (read(fd, &result, sizeof(result)) != sizeof(result)) { 32 | log_error("failed to read from socket %d: %m", fd); 33 | return -1; 34 | } 35 | 36 | if (result) { 37 | return -1; 38 | } 39 | 40 | log_debug("switching to uid %d / gid %d...", uid, uid); 41 | 42 | log_debug("setting uid and gid mappings..."); 43 | if (setgroups(1, &(gid_t){uid}) || setresgid(uid, uid, uid) || 44 | setresuid(uid, uid, uid)) { 45 | log_error("failed to set uid %d / gid %d mappings: %m", uid, uid); 46 | return -1; 47 | } 48 | 49 | log_debug("user namespace set"); 50 | 51 | return 0; 52 | } 53 | 54 | // Listens for the child process to request setting uid / gid, then updates the 55 | // uid_map / gid_map for the child process to use. uid_map and gid_map are a 56 | // Linux kernel mechanism for mapping uids and gids between the parent and child 57 | // parent. The parent process must be privileged to set the uid_map / gid_map. 58 | int user_namespace_prepare_mappings(pid_t pid, int fd) { 59 | int map_fd = 0; 60 | int unshared = -1; 61 | 62 | log_debug("updating uid_map / gid_map..."); 63 | log_debug("retrieving user namespaces status..."); 64 | if (read(fd, &unshared, sizeof(unshared)) != sizeof(unshared)) { 65 | log_error("failed to retrieve status from socket %d: %m", fd); 66 | return -1; 67 | } 68 | 69 | if (!unshared) { 70 | char dir[PATH_MAX] = {0}; 71 | 72 | log_debug("user namespaces enabled"); 73 | 74 | log_debug("writing uid_map / gid_map..."); 75 | for (char **file = (char *[]){"uid_map", "gid_map", 0}; *file; file++) { 76 | if (snprintf(dir, sizeof(dir), "/proc/%d/%s", pid, *file) > 77 | (int)sizeof(dir)) { 78 | log_error("failed to setup dir %s: %m", dir); 79 | return -1; 80 | } 81 | 82 | log_debug("writing %s...", dir); 83 | if ((map_fd = open(dir, O_WRONLY)) == -1) { 84 | log_error("failed to open %s: %m", dir); 85 | return -1; 86 | } 87 | 88 | log_debug("writing settings..."); 89 | 90 | // The first number is the starting uid / gid of the parent 91 | // namespace, the second number is the starting uid / gid of the child 92 | // namespace, and the third number is the number of uids / gids to map. 93 | // This configuration tells the kernel that the preant uid / gid 0 is 94 | // mapped to USER_NAMESPACE_UID_CHILD_RANGE_START uid of the child 95 | if (dprintf(map_fd, "%d %d %d\n", USER_NAMESPACE_UID_PARENT_RANGE_START, 96 | USER_NAMESPACE_UID_CHILD_RANGE_START, 97 | USER_NAMESPACE_UID_CHILD_RANGE_SIZE) == -1) { 98 | log_error("failed to write uid_map '%d': %m", map_fd); 99 | close(map_fd); 100 | return -1; 101 | } 102 | 103 | close(map_fd); 104 | } 105 | 106 | log_debug("uid_map and gid_map updated"); 107 | } 108 | 109 | log_debug("updating socket..."); 110 | if (write(fd, &(int){0}, sizeof(int)) != sizeof(int)) { 111 | log_error("failed to update socket %d: %m", fd); 112 | return -1; 113 | } 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /tests/barco_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int barco_suite_init(void) { return 0; } 8 | int barco_suite_clean(void) { return 0; } 9 | void barco_test(void) { CU_ASSERT(1 == 1); } 10 | 11 | // The main() function for setting up and running the tests. 12 | // Returns a CUE_SUCCESS on successful running, another 13 | // CUnit error code on failure. 14 | int main(void) { 15 | CU_pSuite pSuite = NULL; 16 | 17 | /* initialize the CUnit test registry */ 18 | if (CUE_SUCCESS != CU_initialize_registry()) { 19 | return CU_get_error(); 20 | } 21 | 22 | // Add a suite to the registry 23 | pSuite = CU_add_suite("barco_suite", barco_suite_init, barco_suite_clean); 24 | if (NULL == pSuite) { 25 | CU_cleanup_registry(); 26 | return CU_get_error(); 27 | } 28 | 29 | // Add the tests to the suite 30 | if ((NULL == CU_add_test(pSuite, "test of barco", barco_test))) { 31 | CU_cleanup_registry(); 32 | return CU_get_error(); 33 | } 34 | 35 | // Run all tests using the CUnit Basic interface 36 | CU_basic_set_mode(CU_BRM_VERBOSE); 37 | CU_basic_run_tests(); 38 | CU_cleanup_registry(); 39 | return CU_get_error(); 40 | } 41 | --------------------------------------------------------------------------------