Bring libc implementation of tuxreaperd up to speed with asm version, Backported the race-free wait loop from tuxreaperdasm.c

This commit is contained in:
mrkmntal 2026-08-28 15:14:55 -04:00
commit cdf530334f
4 changed files with 34 additions and 13 deletions

View file

@ -1,6 +1,6 @@
# Tux-Dock Development Log # Tux-Dock Development Log
## 0.2-beta ## 0.3-beta
This release introduces `tuxreaperd`, a micro init system for containers. This release introduces `tuxreaperd`, a micro init system for containers.

View file

@ -52,9 +52,12 @@ sudo ./build/tux-dock
# Override the report server port # Override the report server port
./compile.sh --web-test-view 9000 ./compile.sh --web-test-view 9000
# Force the libc-based tuxreaperd implementation
./compile.sh --force-libc-reaper
``` ```
`compile.sh` builds the `tuxreaperd` micro init before the main project. On supported hosts (`x86_64`/`amd64`/`aarch64`/`arm64`) it first tries the freestanding syscall implementation in `tuxreaperdasm.c`; on other architectures, or if the freestanding build fails, it falls back to the standard POSIX implementation in `tuxreaperdgnu.c`. `compile.sh` builds the `tuxreaperd` micro init before the main project. On supported hosts (`x86_64`/`amd64`/`aarch64`/`arm64`) it first tries the freestanding syscall implementation in `tuxreaperdasm.c`; on other architectures, or if the freestanding build fails, it falls back to the standard POSIX implementation in `tuxreaperdgnu.c`. Pass `--force-libc-reaper` to skip the freestanding build and use the libc implementation directly.
The web report is generated under `/tmp` and includes exact CTest output, test summaries, Docker integration output, a text rendition of the TUI flow, and the tuxreaperd zombie-reaping results. It requires `nc`, `netcat`, or Nmap's `ncat`; press `Ctrl-C` to stop the server. The default port is `8095`; pass a port after `--web-test-view` to override it. `--web-test-view --no-test` is invalid. Normal test runs include a Docker integration test using `debian:forky`, so Docker must be available. On systems with 1 GB or less of available memory, the script automatically uses a smaller build configuration and a single build job. The web report is generated under `/tmp` and includes exact CTest output, test summaries, Docker integration output, a text rendition of the TUI flow, and the tuxreaperd zombie-reaping results. It requires `nc`, `netcat`, or Nmap's `ncat`; press `Ctrl-C` to stop the server. The default port is `8095`; pass a port after `--web-test-view` to override it. `--web-test-view --no-test` is invalid. Normal test runs include a Docker integration test using `debian:forky`, so Docker must be available. On systems with 1 GB or less of available memory, the script automatically uses a smaller build configuration and a single build job.
@ -122,7 +125,7 @@ ctest --test-dir build --output-on-failure
## About / Version ## About / Version
- Version: `0.2-beta` - Version: `0.3-beta`
- Created by: `markmental` - Created by: `markmental`
- GitHub: https://github.com/MARKMENTAL/tuxdock - GitHub: https://github.com/MARKMENTAL/tuxdock
- Forgejo: https://mentalnet.xyz/forgejo-v2/markmental/tuxdock - Forgejo: https://mentalnet.xyz/forgejo-v2/markmental/tuxdock

View file

@ -5,6 +5,7 @@ set -eu
run_tests=1 run_tests=1
web_view=0 web_view=0
web_port=8095 web_port=8095
force_libc_reaper=0
memory_limit=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || true) memory_limit=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || true)
if [ -z "$memory_limit" ] || [ "$memory_limit" = max ]; then if [ -z "$memory_limit" ] || [ "$memory_limit" = max ]; then
@ -44,8 +45,9 @@ while [ "$#" -gt 0 ]; do
shift shift
fi fi
;; ;;
--force-libc-reaper) force_libc_reaper=1 ;;
*) *)
printf '%s\n' "Usage: $0 [--no-test] [--web-test-view [port]]" >&2 printf '%s\n' "Usage: $0 [--no-test] [--web-test-view [port]] [--force-libc-reaper]" >&2
exit 2 exit 2
;; ;;
esac esac
@ -81,18 +83,24 @@ command -v gcc >/dev/null 2>&1 || {
host_arch=$(uname -m) host_arch=$(uname -m)
asm_archs='x86_64 amd64 aarch64 arm64' asm_archs='x86_64 amd64 aarch64 arm64'
page_size=$(getconf PAGE_SIZE 2>/dev/null || printf '%s\n' 4096)
build_tuxreaperd() { build_tuxreaperd() {
case " $asm_archs " in case " $asm_archs " in
*" $host_arch "*) *" $host_arch "*)
printf '%s\n' "[compile] Building freestanding tuxreaperd for $host_arch" if [ "$force_libc_reaper" -eq 0 ]; then
if gcc -nostdlib -static -fno-stack-protector -fno-asynchronous-unwind-tables \ printf '%s\n' "[compile] Building freestanding tuxreaperd for $host_arch"
-fno-builtin -O2 -s -Wl,--build-id=none -Wl,-z,noseparate-code \ if gcc -nostdlib -static -fno-stack-protector -fno-asynchronous-unwind-tables \
-Wall -Wextra tuxreaperdasm.c -o build/tuxreaperd 2>/tmp/tuxreaperd-asm-build.log; then -fno-builtin -O2 -s -Wl,--build-id=none -Wl,-z,noseparate-code \
printf '%s\n' "[compile] Freestanding tuxreaperd built successfully" -Wl,-z,max-page-size=${page_size} \
return 0 -Wall -Wextra tuxreaperdasm.c -o build/tuxreaperd 2>/tmp/tuxreaperd-asm-build.log; then
printf '%s\n' "[compile] Freestanding tuxreaperd built successfully"
return 0
fi
printf '%s\n' "[compile] Freestanding build failed, falling back to libc implementation" >&2
cat /tmp/tuxreaperd-asm-build.log >&2
else
printf '%s\n' "[compile] --force-libc-reaper set, building libc tuxreaperd"
fi fi
printf '%s\n' "[compile] Freestanding build failed, falling back to libc implementation" >&2
cat /tmp/tuxreaperd-asm-build.log >&2
;; ;;
esac esac

View file

@ -59,6 +59,15 @@ int main(int argc, char *argv[]) {
sa_chld.sa_flags = SA_RESTART | SA_NOCLDSTOP; sa_chld.sa_flags = SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa_chld, NULL); sigaction(SIGCHLD, &sa_chld, NULL);
// Block the signals we wait on so the check/suspend loop is race-free
sigset_t block_mask, old_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigaddset(&block_mask, SIGTERM);
sigaddset(&block_mask, SIGINT);
sigaddset(&block_mask, SIGHUP);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
// Spawn the primary workload // Spawn the primary workload
g_main_child = fork(); g_main_child = fork();
if (g_main_child < 0) { if (g_main_child < 0) {
@ -67,6 +76,7 @@ int main(int argc, char *argv[]) {
} }
if (g_main_child == 0) { if (g_main_child == 0) {
sigprocmask(SIG_SETMASK, &old_mask, NULL);
execvp(argv[1], &argv[1]); execvp(argv[1], &argv[1]);
perror("execvp"); perror("execvp");
_exit(127); _exit(127);
@ -74,7 +84,7 @@ int main(int argc, char *argv[]) {
// Block until the primary process exits // Block until the primary process exits
while (!g_child_exited) { while (!g_child_exited) {
pause(); sigsuspend(&old_mask);
} }
// Final sweep of any remaining lingering zombies // Final sweep of any remaining lingering zombies