├── .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 |
--------------------------------------------------------------------------------