Validate Shepherd services on FreeBSD
This commit is contained in:
@@ -1266,3 +1266,86 @@ Next recommended step:
|
||||
2. carry forward the separate real-checkout runtime blocker for later integration work:
|
||||
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
|
||||
3. continue using `/frx/store` rather than `/gnu/store` for future FreeBSD integration experiments when a persistent store root is required
|
||||
|
||||
## 2026-04-01 — Phase 4.1 completed: Shepherd built and validated as a regular FreeBSD service manager
|
||||
|
||||
Completed work:
|
||||
|
||||
- added a reproducible local Guile Fibers build harness:
|
||||
- `tests/shepherd/build-local-guile-fibers.sh`
|
||||
- added a reproducible local Shepherd build harness:
|
||||
- `tests/shepherd/build-local-shepherd.sh`
|
||||
- added a runnable multi-service Shepherd validation harness for FreeBSD:
|
||||
- `tests/shepherd/run-freebsd-shepherd-service-prototype.sh`
|
||||
- wrote the Phase 4.1 report:
|
||||
- `docs/reports/phase4-freebsd-shepherd-service.md`
|
||||
- ran the service-management prototype successfully and captured metadata under:
|
||||
- `/tmp/freebsd-shepherd-service-metadata.txt`
|
||||
|
||||
Important findings:
|
||||
|
||||
- the current FreeBSD path now has a working local Shepherd build based on:
|
||||
- local fixed Guile
|
||||
- locally installed Guile Fibers `1.4.2`
|
||||
- Shepherd `1.0.9`
|
||||
- Shepherd build/install required one concrete FreeBSD-specific toolchain adaptation:
|
||||
- `SED=/usr/local/bin/gsed`
|
||||
because the install phase edits wrapper scripts using GNU `sed -i` syntax that base FreeBSD `sed` does not accept
|
||||
- at runtime, Shepherd reports:
|
||||
- `System lacks support for 'signalfd'; using fallback mechanism.`
|
||||
but the fallback path works correctly for supervision on this host
|
||||
- the prototype successfully validated all requested regular-service capabilities:
|
||||
- start/stop via `herd`
|
||||
- dependency handling
|
||||
- status monitoring
|
||||
- crash/respawn behavior
|
||||
- privilege-aware execution
|
||||
- the concrete service set used for validation included:
|
||||
- an unprivileged heartbeat logger
|
||||
- a loopback HTTP service
|
||||
- a dependent file-monitor service
|
||||
- a crash-once respawn test service
|
||||
- observed metadata confirmed:
|
||||
- `logger_running=yes`
|
||||
- `web_running=yes`
|
||||
- `monitor_running=yes`
|
||||
- `crashy_running=yes`
|
||||
- `logger_uid=65534` (`nobody`)
|
||||
- `http_response=shepherd-freebsd-ok`
|
||||
- `monitor_detected=detected`
|
||||
- `crashy_counter=2`
|
||||
|
||||
Current assessment:
|
||||
|
||||
- Phase 4.1 is now satisfied on the current FreeBSD prototype track
|
||||
- Shepherd is no longer just a theoretical later step; it now builds and supervises multiple services correctly on the host when paired with the fixed local Guile stack
|
||||
- the next question is no longer “can Shepherd run on FreeBSD at all?” but “what is the best FreeBSD init-integration strategy for it on this prototype path?”
|
||||
|
||||
Recent commits:
|
||||
|
||||
- `e380e88` — `Add FreeBSD Guile verification harness`
|
||||
- `cd721b1` — `Update progress after Guile verification`
|
||||
- `27916cb` — `Diagnose Guile subprocess crash on FreeBSD`
|
||||
- `02f7a7f` — `Validate local Guile fix on FreeBSD`
|
||||
- `4aebea4` — `Add native GNU Hello FreeBSD build harness`
|
||||
- `c944cdb` — `Validate Guix builder phases on FreeBSD`
|
||||
- `0a2e48e` — `Validate GNU which builder phases on FreeBSD`
|
||||
- `245a47d` — `Document gaps to real Guix FreeBSD builds`
|
||||
- `d62e9b0` — `Investigate Guix derivation generation on FreeBSD`
|
||||
- `c0a85ed` — `Build local Guile-GnuTLS on FreeBSD`
|
||||
- `15b9037` — `Build local Guile-Git on FreeBSD`
|
||||
- `47d31e8` — `Build local Guile-JSON on FreeBSD`
|
||||
- `d82195b` — `Advance Guix checkout on FreeBSD`
|
||||
- `9bf3d30` — `Document FreeBSD syscall mapping`
|
||||
- `7621798` — `Prototype FreeBSD jail build isolation`
|
||||
- `d65b2af` — `Prototype FreeBSD build user isolation`
|
||||
- `e404e2e` — `Prototype FreeBSD store management`
|
||||
- `eb0d77c` — `Adapt GNU build phases for FreeBSD`
|
||||
- `d47dc9b` — `Prototype FreeBSD package definitions`
|
||||
|
||||
Next recommended step:
|
||||
|
||||
1. complete Phase 4.2 by prototyping how Shepherd should be launched and stopped through FreeBSD init conventions while validating boot/shutdown dependency ordering for essential services
|
||||
2. after that, bridge Shepherd to key FreeBSD service concepts such as rc.d management, loopback/network configuration, filesystem setup, and temporary user/group administration
|
||||
3. continue carrying the separate real-checkout runtime blocker for later integration work:
|
||||
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
|
||||
|
||||
171
docs/reports/phase4-freebsd-shepherd-service.md
Normal file
171
docs/reports/phase4-freebsd-shepherd-service.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Phase 4.1: Shepherd built and validated as a regular FreeBSD service manager
|
||||
|
||||
Date: 2026-04-01
|
||||
|
||||
## Summary
|
||||
|
||||
This step validates that GNU Shepherd can be built on the current FreeBSD amd64 host using the previously fixed local Guile stack and can successfully manage multiple services as a regular daemon.
|
||||
|
||||
Added files:
|
||||
|
||||
- `tests/shepherd/build-local-guile-fibers.sh`
|
||||
- `tests/shepherd/build-local-shepherd.sh`
|
||||
- `tests/shepherd/run-freebsd-shepherd-service-prototype.sh`
|
||||
|
||||
## Build inputs and versions
|
||||
|
||||
The validation used:
|
||||
|
||||
- fixed local Guile:
|
||||
- `/tmp/guile-freebsd-validate-install/bin/guile`
|
||||
- shared local Guile extension prefix:
|
||||
- `/tmp/guile-gnutls-freebsd-validate-install`
|
||||
- Guile Fibers from the current Guix source of truth:
|
||||
- version `1.4.2`
|
||||
- resolved commit `297359f0ad655378bcc3ff0d4e96101965ef39b4`
|
||||
- Shepherd from the current Guix package definition:
|
||||
- version `1.0.9`
|
||||
- nix-base32 `1mh080060lnycys8yq6kkiy363wif8dsip3nyklgd3a1r22wb274`
|
||||
- verified SHA256 `e488c585c8418df6e8f476dca81b72910f337c9cd3608fb467de5260004000d6`
|
||||
|
||||
## FreeBSD-specific build findings
|
||||
|
||||
### Guile Fibers
|
||||
|
||||
- building from the Guix-matching Git tag required autotools regeneration:
|
||||
- `autoreconf -vfi`
|
||||
- the resulting installation validated successfully with:
|
||||
- `(use-modules (fibers))`
|
||||
|
||||
### Shepherd
|
||||
|
||||
- Shepherd `1.0.9` configured and built successfully against the fixed local Guile plus the locally installed Fibers module
|
||||
- one FreeBSD-specific build adaptation was required during install:
|
||||
- configure must use `SED=/usr/local/bin/gsed`
|
||||
- reason:
|
||||
- the Shepherd install phase edits installed wrapper scripts with GNU `sed -i` syntax
|
||||
- base FreeBSD `/usr/bin/sed` rejects that invocation
|
||||
- after using GNU `sed`, install completed successfully
|
||||
|
||||
## Runtime findings on FreeBSD
|
||||
|
||||
The installed Shepherd and herd commands run successfully on FreeBSD with the local Guile environment.
|
||||
|
||||
Observed runtime note:
|
||||
|
||||
- Shepherd prints:
|
||||
- `System lacks support for 'signalfd'; using fallback mechanism.`
|
||||
|
||||
This is expected on FreeBSD and did not prevent service supervision from working.
|
||||
|
||||
## Service-management prototype
|
||||
|
||||
Run command:
|
||||
|
||||
```sh
|
||||
METADATA_OUT=/tmp/freebsd-shepherd-service-metadata.txt \
|
||||
./tests/shepherd/run-freebsd-shepherd-service-prototype.sh
|
||||
```
|
||||
|
||||
The prototype starts a root-launched Shepherd instance and validates four services:
|
||||
|
||||
1. `logger`
|
||||
- background service
|
||||
- runs as user/group `nobody:nobody`
|
||||
- writes heartbeat output to a log
|
||||
2. `web`
|
||||
- depends on `logger`
|
||||
- serves a tiny HTTP response over loopback using `nc`
|
||||
3. `file-monitor`
|
||||
- depends on `web`
|
||||
- watches for a flag file and records detection
|
||||
4. `crashy`
|
||||
- fails on first start
|
||||
- then respawns and stays running
|
||||
|
||||
## Verified behaviors
|
||||
|
||||
### Start/stop through Shepherd command interface
|
||||
|
||||
The prototype successfully used `herd` to:
|
||||
|
||||
- start services
|
||||
- inspect service status
|
||||
- stop the entire service graph through `stop root`
|
||||
|
||||
### Dependency handling
|
||||
|
||||
Starting `file-monitor` automatically started its dependencies:
|
||||
|
||||
- `logger`
|
||||
- `web`
|
||||
|
||||
All three were then reported as running by `herd status`.
|
||||
|
||||
### Service status monitoring
|
||||
|
||||
Recorded metadata confirmed:
|
||||
|
||||
- `logger_running=yes`
|
||||
- `web_running=yes`
|
||||
- `monitor_running=yes`
|
||||
- `crashy_running=yes`
|
||||
|
||||
### Crash handling and restart
|
||||
|
||||
The `crashy` service was configured with respawn enabled.
|
||||
|
||||
Observed behavior:
|
||||
|
||||
- first launch exited with code `1`
|
||||
- Shepherd logged a respawn
|
||||
- second launch remained running
|
||||
- observed metadata:
|
||||
- `crashy_counter=2`
|
||||
|
||||
### Privilege handling
|
||||
|
||||
The `logger`, `web`, and `file-monitor` services were launched by a root-owned Shepherd instance but executed as `nobody`.
|
||||
|
||||
Observed metadata:
|
||||
|
||||
- `logger_uid=65534`
|
||||
|
||||
This matches FreeBSD `nobody` on the host.
|
||||
|
||||
### Concrete service execution checks
|
||||
|
||||
The loopback HTTP service returned the expected deterministic response:
|
||||
|
||||
- `http_response=shepherd-freebsd-ok`
|
||||
|
||||
The file-monitor service detected a watched-file event successfully:
|
||||
|
||||
- `monitor_detected=detected`
|
||||
|
||||
## Why this satisfies Phase 4.1
|
||||
|
||||
Phase 4.1 required that Shepherd compile and run on FreeBSD as a regular service manager and demonstrate:
|
||||
|
||||
- service start/stop
|
||||
- dependency management
|
||||
- service status monitoring
|
||||
- crash/restart handling
|
||||
- appropriate privilege execution
|
||||
|
||||
Those requirements are satisfied on the current prototype track because:
|
||||
|
||||
- Shepherd now builds reproducibly with the fixed local Guile stack
|
||||
- a root-launched Shepherd instance successfully supervised multiple services on FreeBSD
|
||||
- dependencies were honored
|
||||
- statuses were queryable through `herd`
|
||||
- a crashing service was respawned successfully
|
||||
- services were executed under an unprivileged account where requested
|
||||
|
||||
## Conclusion
|
||||
|
||||
Phase 4.1 is satisfied on the current FreeBSD prototype track:
|
||||
|
||||
- Shepherd builds on FreeBSD with a small GNU `sed` install-time adjustment
|
||||
- the lack of `signalfd` is handled by Shepherd's fallback path
|
||||
- regular-daemon service supervision works correctly for multiple dependent services on the host
|
||||
174
tests/shepherd/build-local-guile-fibers.sh
Executable file
174
tests/shepherd/build-local-guile-fibers.sh
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
fibers_repo=${GUILE_FIBERS_REPO:-"https://codeberg.org/guile/fibers"}
|
||||
fibers_version=${GUILE_FIBERS_VERSION:-1.4.2}
|
||||
install_prefix=${INSTALL_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}
|
||||
guile_bin=${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}
|
||||
make_bin=${MAKE_BIN:-gmake}
|
||||
|
||||
if [ ! -x "$guile_bin" ]; then
|
||||
echo "Guile binary is not executable: $guile_bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
for tool in git autoreconf pkg-config "$make_bin"; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "Required tool not found: $tool" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cleanup=0
|
||||
if [ -n "${WORKDIR:-}" ]; then
|
||||
workdir=$WORKDIR
|
||||
mkdir -p "$workdir"
|
||||
else
|
||||
workdir=$(mktemp -d /tmp/fruix-guile-fibers.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
cleanup_workdir() {
|
||||
if [ "$cleanup" -eq 1 ]; then
|
||||
rm -rf "$workdir"
|
||||
fi
|
||||
}
|
||||
trap cleanup_workdir EXIT INT TERM
|
||||
|
||||
guile_bindir=$(CDPATH= cd -- "$(dirname "$guile_bin")" && pwd)
|
||||
guile_prefix=$(CDPATH= cd -- "$guile_bindir/.." && pwd)
|
||||
guile_lib_dir=$guile_prefix/lib
|
||||
guile_version=$(LD_LIBRARY_PATH="$guile_lib_dir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$guile_bin" -c '(display (effective-version))')
|
||||
site_dir=$install_prefix/share/guile/site/$guile_version
|
||||
site_ccache_dir=$install_prefix/lib/guile/$guile_version/site-ccache
|
||||
extensions_dir=$install_prefix/lib/guile/$guile_version/extensions
|
||||
|
||||
tool_bindir=$workdir/guile-tools-bin
|
||||
mkdir -p "$tool_bindir"
|
||||
ln -sf "$guile_bin" "$tool_bindir/guile-3.0"
|
||||
ln -sf "$guile_bin" "$tool_bindir/guile"
|
||||
ln -sf "$guile_bindir/guild" "$tool_bindir/guild-3.0"
|
||||
ln -sf "$guile_bindir/guild" "$tool_bindir/guild"
|
||||
ln -sf "$guile_bindir/guile-config" "$tool_bindir/guile-config-3.0"
|
||||
ln -sf "$guile_bindir/guile-config" "$tool_bindir/guile-config"
|
||||
ln -sf "$guile_bindir/guile-snarf" "$tool_bindir/guile-snarf"
|
||||
export PATH="$tool_bindir:$guile_bindir:/usr/local/bin:$PATH"
|
||||
export ACLOCAL_PATH=/usr/local/share/aclocal${ACLOCAL_PATH:+:$ACLOCAL_PATH}
|
||||
export PKG_CONFIG_PATH=$install_prefix/lib/pkgconfig:$install_prefix/libdata/pkgconfig:/usr/local/libdata/pkgconfig:/usr/local/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
|
||||
export CPPFLAGS="-I$guile_prefix/include -I$install_prefix/include -I/usr/local/include"
|
||||
export LDFLAGS="-L$guile_lib_dir -L$install_prefix/lib -L/usr/local/lib -Wl,-rpath,$guile_lib_dir -Wl,-rpath,$install_prefix/lib -Wl,-rpath,/usr/local/lib"
|
||||
if [ -n "${LD_LIBRARY_PATH:-}" ]; then
|
||||
export LD_LIBRARY_PATH="$install_prefix/lib:$guile_lib_dir:/usr/local/lib:$LD_LIBRARY_PATH"
|
||||
else
|
||||
export LD_LIBRARY_PATH="$install_prefix/lib:$guile_lib_dir:/usr/local/lib"
|
||||
fi
|
||||
if [ -d "$site_dir" ]; then
|
||||
export GUILE_LOAD_PATH="$site_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}"
|
||||
fi
|
||||
if [ -d "$site_ccache_dir" ]; then
|
||||
export GUILE_LOAD_COMPILED_PATH="$site_ccache_dir${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}"
|
||||
fi
|
||||
if [ -d "$extensions_dir" ]; then
|
||||
export GUILE_EXTENSIONS_PATH="$extensions_dir${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}"
|
||||
fi
|
||||
|
||||
src_dir=$workdir/guile-fibers
|
||||
build_dir=$workdir/build
|
||||
metadata_file=$workdir/guile-fibers-build-metadata.txt
|
||||
env_file=$workdir/guile-fibers-env.sh
|
||||
clone_log=$workdir/clone.log
|
||||
autoreconf_log=$workdir/autoreconf.log
|
||||
configure_log=$workdir/configure.log
|
||||
build_log=$workdir/build.log
|
||||
install_log=$workdir/install.log
|
||||
|
||||
printf 'Using Guile: %s\n' "$guile_bin"
|
||||
printf 'Installing into existing prefix: %s\n' "$install_prefix"
|
||||
printf 'Working directory: %s\n' "$workdir"
|
||||
|
||||
rm -rf \
|
||||
"$site_dir/fibers" \
|
||||
"$site_dir/fibers.scm" \
|
||||
"$site_ccache_dir/fibers" \
|
||||
"$site_ccache_dir/fibers.go"
|
||||
mkdir -p "$install_prefix"
|
||||
|
||||
git clone --depth 1 --branch "v$fibers_version" "$fibers_repo" "$src_dir" >"$clone_log" 2>&1
|
||||
fibers_commit=$(git -C "$src_dir" rev-parse HEAD)
|
||||
(
|
||||
cd "$src_dir"
|
||||
autoreconf -vfi
|
||||
) >"$autoreconf_log" 2>&1
|
||||
mkdir -p "$build_dir"
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$src_dir/configure" --prefix="$install_prefix"
|
||||
) >"$configure_log" 2>&1
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$make_bin" GUILE_AUTO_COMPILE=0 -j"${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 1)}"
|
||||
) >"$build_log" 2>&1
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$make_bin" GUILE_AUTO_COMPILE=0 install
|
||||
) >"$install_log" 2>&1
|
||||
|
||||
export GUILE_LOAD_PATH="$site_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}"
|
||||
export GUILE_LOAD_COMPILED_PATH="$site_ccache_dir${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}"
|
||||
module_check=$($guile_bin -c '(use-modules (fibers)) (display "ok") (newline)')
|
||||
if [ "$module_check" != "ok" ]; then
|
||||
echo "(fibers) module validation failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat >"$env_file" <<EOF
|
||||
export GUILE_EXTRA_PREFIX='$install_prefix'
|
||||
export GUILE_LOAD_PATH='$site_dir'
|
||||
export GUILE_LOAD_COMPILED_PATH='$site_ccache_dir'
|
||||
export GUILE_EXTENSIONS_PATH='$extensions_dir'
|
||||
export LD_LIBRARY_PATH='$install_prefix/lib:$guile_lib_dir:/usr/local/lib'
|
||||
EOF
|
||||
|
||||
cat >"$metadata_file" <<EOF
|
||||
fibers_repo=$fibers_repo
|
||||
fibers_version=$fibers_version
|
||||
fibers_commit=$fibers_commit
|
||||
guile_bin=$guile_bin
|
||||
guile_version=$guile_version
|
||||
install_prefix=$install_prefix
|
||||
site_dir=$site_dir
|
||||
site_ccache_dir=$site_ccache_dir
|
||||
extensions_dir=$extensions_dir
|
||||
clone_log=$clone_log
|
||||
autoreconf_log=$autoreconf_log
|
||||
configure_log=$configure_log
|
||||
build_log=$build_log
|
||||
install_log=$install_log
|
||||
module_check=$module_check
|
||||
env_file=$env_file
|
||||
EOF
|
||||
|
||||
if [ -n "${METADATA_OUT:-}" ]; then
|
||||
mkdir -p "$(dirname "$METADATA_OUT")"
|
||||
cp "$metadata_file" "$METADATA_OUT"
|
||||
fi
|
||||
if [ -n "${ENV_OUT:-}" ]; then
|
||||
mkdir -p "$(dirname "$ENV_OUT")"
|
||||
cp "$env_file" "$ENV_OUT"
|
||||
fi
|
||||
|
||||
printf 'PASS local-guile-fibers-build\n'
|
||||
printf 'Module check: %s\n' "$module_check"
|
||||
printf 'Environment file: %s\n' "$env_file"
|
||||
printf 'Metadata file: %s\n' "$metadata_file"
|
||||
if [ -n "${ENV_OUT:-}" ]; then
|
||||
printf 'Copied environment file to: %s\n' "$ENV_OUT"
|
||||
fi
|
||||
if [ -n "${METADATA_OUT:-}" ]; then
|
||||
printf 'Copied metadata file to: %s\n' "$METADATA_OUT"
|
||||
fi
|
||||
printf '%s\n' '--- metadata ---'
|
||||
cat "$metadata_file"
|
||||
220
tests/shepherd/build-local-shepherd.sh
Executable file
220
tests/shepherd/build-local-shepherd.sh
Executable file
@@ -0,0 +1,220 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
source_url=${SHEPHERD_SOURCE_URL:-"https://ftp.gnu.org/gnu/shepherd/shepherd-1.0.9.tar.gz"}
|
||||
version=${SHEPHERD_VERSION:-1.0.9}
|
||||
expected_nix_base32=${SHEPHERD_NIX_BASE32:-1mh080060lnycys8yq6kkiy363wif8dsip3nyklgd3a1r22wb274}
|
||||
install_prefix=${INSTALL_PREFIX:-/tmp/shepherd-freebsd-validate-install}
|
||||
guile_bin=${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}
|
||||
guile_extra_prefix=${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}
|
||||
guix_source_dir=${GUIX_SOURCE_DIR:-"$HOME/repos/guix"}
|
||||
make_bin=${MAKE_BIN:-gmake}
|
||||
gsed_bin=${GSED_BIN:-/usr/local/bin/gsed}
|
||||
|
||||
if [ ! -x "$guile_bin" ]; then
|
||||
echo "Guile binary is not executable: $guile_bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$guix_source_dir/guix" ]; then
|
||||
echo "Guix source tree not found at $guix_source_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
for tool in fetch sha256 bsdtar "$make_bin" "$gsed_bin"; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "Required tool not found: $tool" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cleanup=0
|
||||
if [ -n "${WORKDIR:-}" ]; then
|
||||
workdir=$WORKDIR
|
||||
mkdir -p "$workdir"
|
||||
else
|
||||
workdir=$(mktemp -d /tmp/fruix-shepherd-build.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
cleanup_workdir() {
|
||||
if [ "$cleanup" -eq 1 ]; then
|
||||
rm -rf "$workdir"
|
||||
fi
|
||||
}
|
||||
trap cleanup_workdir EXIT INT TERM
|
||||
|
||||
guile_bindir=$(CDPATH= cd -- "$(dirname "$guile_bin")" && pwd)
|
||||
guile_prefix=$(CDPATH= cd -- "$guile_bindir/.." && pwd)
|
||||
guile_lib_dir=$guile_prefix/lib
|
||||
guile_version=$(LD_LIBRARY_PATH="$guile_lib_dir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$guile_bin" -c '(display (effective-version))')
|
||||
guile_extra_site_dir=$guile_extra_prefix/share/guile/site/$guile_version
|
||||
guile_extra_site_ccache_dir=$guile_extra_prefix/lib/guile/$guile_version/site-ccache
|
||||
guile_extra_extensions_dir=$guile_extra_prefix/lib/guile/$guile_version/extensions
|
||||
|
||||
tool_bindir=$workdir/guile-tools-bin
|
||||
mkdir -p "$tool_bindir"
|
||||
ln -sf "$guile_bin" "$tool_bindir/guile-3.0"
|
||||
ln -sf "$guile_bin" "$tool_bindir/guile"
|
||||
ln -sf "$guile_bindir/guild" "$tool_bindir/guild-3.0"
|
||||
ln -sf "$guile_bindir/guild" "$tool_bindir/guild"
|
||||
ln -sf "$guile_bindir/guile-config" "$tool_bindir/guile-config-3.0"
|
||||
ln -sf "$guile_bindir/guile-config" "$tool_bindir/guile-config"
|
||||
ln -sf "$guile_bindir/guile-snarf" "$tool_bindir/guile-snarf"
|
||||
export PATH="$tool_bindir:$guile_bindir:/usr/local/bin:$PATH"
|
||||
export ACLOCAL_PATH=/usr/local/share/aclocal${ACLOCAL_PATH:+:$ACLOCAL_PATH}
|
||||
export PKG_CONFIG_PATH=$guile_extra_prefix/lib/pkgconfig:$guile_extra_prefix/libdata/pkgconfig:$guile_prefix/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/local/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
|
||||
export CPPFLAGS="-I$guile_prefix/include -I$guile_extra_prefix/include -I/usr/local/include"
|
||||
export LDFLAGS="-L$guile_lib_dir -L$guile_extra_prefix/lib -L/usr/local/lib -Wl,-rpath,$guile_lib_dir -Wl,-rpath,$guile_extra_prefix/lib -Wl,-rpath,/usr/local/lib"
|
||||
if [ -n "${LD_LIBRARY_PATH:-}" ]; then
|
||||
export LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib:$LD_LIBRARY_PATH"
|
||||
else
|
||||
export LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib"
|
||||
fi
|
||||
if [ -d "$guile_extra_site_dir" ]; then
|
||||
export GUILE_LOAD_PATH="$guile_extra_site_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}"
|
||||
fi
|
||||
if [ -d "$guile_extra_site_ccache_dir" ]; then
|
||||
export GUILE_LOAD_COMPILED_PATH="$guile_extra_site_ccache_dir${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}"
|
||||
fi
|
||||
if [ -d "$guile_extra_extensions_dir" ]; then
|
||||
export GUILE_EXTENSIONS_PATH="$guile_extra_extensions_dir${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}"
|
||||
fi
|
||||
|
||||
metadata_file=$workdir/shepherd-build-metadata.txt
|
||||
env_file=$workdir/shepherd-env.sh
|
||||
src_tarball=$workdir/shepherd-$version.tar.gz
|
||||
src_dir=$workdir/shepherd-$version
|
||||
build_dir=$workdir/build
|
||||
configure_log=$workdir/configure.log
|
||||
build_log=$workdir/build.log
|
||||
install_log=$workdir/install.log
|
||||
version_log=$workdir/version.log
|
||||
|
||||
expected_sha256_hex=$(GUILE_AUTO_COMPILE=0 \
|
||||
GUILE_LOAD_PATH="$guix_source_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}" \
|
||||
"$guile_bin" -c "(use-modules (guix base32) (rnrs bytevectors) (ice-9 format)) (for-each (lambda (b) (format #t \"~2,'0x\" b)) (bytevector->u8-list (nix-base32-string->bytevector (cadr (command-line))))) (newline)" \
|
||||
"$expected_nix_base32")
|
||||
|
||||
printf 'Using Guile: %s\n' "$guile_bin"
|
||||
printf 'Installing to: %s\n' "$install_prefix"
|
||||
printf 'Working directory: %s\n' "$workdir"
|
||||
printf 'Fetching: %s\n' "$source_url"
|
||||
fetch -o "$src_tarball" "$source_url"
|
||||
|
||||
actual_sha256_hex=$(sha256 -q "$src_tarball")
|
||||
if [ "$actual_sha256_hex" != "$expected_sha256_hex" ]; then
|
||||
echo "sha256 mismatch for $src_tarball" >&2
|
||||
echo "expected: $expected_sha256_hex" >&2
|
||||
echo "actual: $actual_sha256_hex" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$src_dir" "$build_dir" "$install_prefix"
|
||||
mkdir -p "$src_dir" "$build_dir"
|
||||
bsdtar -xf "$src_tarball" -C "$src_dir" --strip-components=1
|
||||
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$src_dir/configure" \
|
||||
--prefix="$install_prefix" \
|
||||
--localstatedir="$install_prefix/var" \
|
||||
SED="$gsed_bin" \
|
||||
GUILE="$guile_bin" \
|
||||
GUILD="$guile_bindir/guild" \
|
||||
GUILE_SNARF="$guile_bindir/guile-snarf"
|
||||
) >"$configure_log" 2>&1
|
||||
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$make_bin" GUILE_AUTO_COMPILE=0 -j"${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 1)}"
|
||||
) >"$build_log" 2>&1
|
||||
|
||||
(
|
||||
cd "$build_dir"
|
||||
"$make_bin" GUILE_AUTO_COMPILE=0 install
|
||||
) >"$install_log" 2>&1
|
||||
|
||||
shepherd_version_output=$(GUILE_LOAD_PATH="$guile_extra_site_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_extra_site_ccache_dir${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extra_extensions_dir${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}" \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
"$install_prefix/bin/shepherd" --version 2>&1 | tee "$version_log")
|
||||
herd_version_output=$(GUILE_LOAD_PATH="$guile_extra_site_dir${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_extra_site_ccache_dir${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extra_extensions_dir${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}" \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
"$install_prefix/bin/herd" --version 2>&1 | tail -n 1)
|
||||
|
||||
case "$shepherd_version_output" in
|
||||
*"shepherd (GNU Shepherd) $version"*) : ;;
|
||||
*)
|
||||
echo "shepherd version validation failed" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$shepherd_version_output" in
|
||||
*"System lacks support for 'signalfd'; using fallback mechanism."*)
|
||||
signalfd_fallback=yes
|
||||
;;
|
||||
*)
|
||||
signalfd_fallback=no
|
||||
;;
|
||||
esac
|
||||
|
||||
cat >"$env_file" <<EOF
|
||||
export SHEPHERD_PREFIX='$install_prefix'
|
||||
export SHEPHERD_BIN='$install_prefix/bin/shepherd'
|
||||
export HERD_BIN='$install_prefix/bin/herd'
|
||||
export GUILE_EXTRA_PREFIX='$guile_extra_prefix'
|
||||
export GUILE_LOAD_PATH='$guile_extra_site_dir'
|
||||
export GUILE_LOAD_COMPILED_PATH='$guile_extra_site_ccache_dir'
|
||||
export GUILE_EXTENSIONS_PATH='$guile_extra_extensions_dir'
|
||||
export LD_LIBRARY_PATH='$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib'
|
||||
EOF
|
||||
|
||||
cat >"$metadata_file" <<EOF
|
||||
version=$version
|
||||
source_url=$source_url
|
||||
expected_nix_base32=$expected_nix_base32
|
||||
expected_sha256_hex=$expected_sha256_hex
|
||||
actual_sha256_hex=$actual_sha256_hex
|
||||
guile_bin=$guile_bin
|
||||
guile_version=$guile_version
|
||||
guile_extra_prefix=$guile_extra_prefix
|
||||
install_prefix=$install_prefix
|
||||
configure_log=$configure_log
|
||||
build_log=$build_log
|
||||
install_log=$install_log
|
||||
version_log=$version_log
|
||||
gsed_bin=$gsed_bin
|
||||
signalfd_fallback=$signalfd_fallback
|
||||
shepherd_version_output=$(printf '%s' "$shepherd_version_output" | tr '\n' ' ')
|
||||
herd_version_output=$(printf '%s' "$herd_version_output" | tr '\n' ' ')
|
||||
env_file=$env_file
|
||||
EOF
|
||||
|
||||
if [ -n "${METADATA_OUT:-}" ]; then
|
||||
mkdir -p "$(dirname "$METADATA_OUT")"
|
||||
cp "$metadata_file" "$METADATA_OUT"
|
||||
fi
|
||||
if [ -n "${ENV_OUT:-}" ]; then
|
||||
mkdir -p "$(dirname "$ENV_OUT")"
|
||||
cp "$env_file" "$ENV_OUT"
|
||||
fi
|
||||
|
||||
printf 'PASS local-shepherd-build\n'
|
||||
printf 'Version output validated for GNU Shepherd %s\n' "$version"
|
||||
printf 'Environment file: %s\n' "$env_file"
|
||||
printf 'Metadata file: %s\n' "$metadata_file"
|
||||
if [ -n "${ENV_OUT:-}" ]; then
|
||||
printf 'Copied environment file to: %s\n' "$ENV_OUT"
|
||||
fi
|
||||
if [ -n "${METADATA_OUT:-}" ]; then
|
||||
printf 'Copied metadata file to: %s\n' "$METADATA_OUT"
|
||||
fi
|
||||
printf '%s\n' '--- metadata ---'
|
||||
cat "$metadata_file"
|
||||
355
tests/shepherd/run-freebsd-shepherd-service-prototype.sh
Executable file
355
tests/shepherd/run-freebsd-shepherd-service-prototype.sh
Executable file
@@ -0,0 +1,355 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
||||
guile_bin=${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}
|
||||
guile_extra_prefix=${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}
|
||||
shepherd_prefix=${SHEPHERD_PREFIX:-/tmp/shepherd-freebsd-validate-install}
|
||||
shepherd_bin=$shepherd_prefix/bin/shepherd
|
||||
herd_bin=$shepherd_prefix/bin/herd
|
||||
metadata_target=${METADATA_OUT:-}
|
||||
|
||||
if [ ! -x "$guile_bin" ]; then
|
||||
echo "Guile binary is not executable: $guile_bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_built() {
|
||||
if [ ! -d "$guile_extra_prefix/share/guile/site" ] || \
|
||||
! GUILE_LOAD_PATH="$guile_extra_prefix/share/guile/site/3.0${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_extra_prefix/lib/guile/3.0/site-ccache${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extra_prefix/lib/guile/3.0/extensions${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}" \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:/tmp/guile-freebsd-validate-install/lib:/usr/local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
"$guile_bin" -c '(catch #t (lambda () (use-modules (fibers)) (display "ok") (newline)) (lambda _ (display "missing") (newline)))' | grep -qx ok; then
|
||||
METADATA_OUT= ENV_OUT= "$repo_root/tests/shepherd/build-local-guile-fibers.sh"
|
||||
fi
|
||||
|
||||
if [ ! -x "$shepherd_bin" ] || [ ! -x "$herd_bin" ]; then
|
||||
METADATA_OUT= ENV_OUT= GUILE_EXTRA_PREFIX="$guile_extra_prefix" "$repo_root/tests/shepherd/build-local-shepherd.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_built
|
||||
|
||||
guile_bindir=$(CDPATH= cd -- "$(dirname "$guile_bin")" && pwd)
|
||||
guile_prefix=$(CDPATH= cd -- "$guile_bindir/.." && pwd)
|
||||
guile_lib_dir=$guile_prefix/lib
|
||||
guile_version=$(LD_LIBRARY_PATH="$guile_lib_dir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$guile_bin" -c '(display (effective-version))')
|
||||
extra_site_dir=$guile_extra_prefix/share/guile/site/$guile_version
|
||||
extra_site_ccache_dir=$guile_extra_prefix/lib/guile/$guile_version/site-ccache
|
||||
extra_extensions_dir=$guile_extra_prefix/lib/guile/$guile_version/extensions
|
||||
|
||||
run_root_env() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
env \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib" \
|
||||
GUILE_LOAD_PATH="$extra_site_dir" \
|
||||
GUILE_LOAD_COMPILED_PATH="$extra_site_ccache_dir" \
|
||||
GUILE_EXTENSIONS_PATH="$extra_extensions_dir" \
|
||||
"$@"
|
||||
else
|
||||
sudo env \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:$guile_lib_dir:/usr/local/lib" \
|
||||
GUILE_LOAD_PATH="$extra_site_dir" \
|
||||
GUILE_LOAD_COMPILED_PATH="$extra_site_ccache_dir" \
|
||||
GUILE_EXTENSIONS_PATH="$extra_extensions_dir" \
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup=0
|
||||
if [ -n "${WORKDIR:-}" ]; then
|
||||
workdir=$WORKDIR
|
||||
mkdir -p "$workdir"
|
||||
else
|
||||
workdir=$(mktemp -d /tmp/freebsd-shepherd-service.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
port=$((19000 + ($$ % 1000)))
|
||||
state_dir=$workdir/state
|
||||
config_file=$workdir/service-config.scm
|
||||
socket_file=$workdir/shepherd.sock
|
||||
pid_file=$workdir/shepherd.pid
|
||||
shepherd_log=$workdir/shepherd.log
|
||||
shepherd_stdout=$workdir/shepherd.out
|
||||
metadata_file=$workdir/freebsd-shepherd-service-metadata.txt
|
||||
logger_status=$workdir/logger.status
|
||||
web_status=$workdir/web.status
|
||||
monitor_status=$workdir/file-monitor.status
|
||||
crashy_status=$workdir/crashy.status
|
||||
service_command_log=$state_dir/service-events.log
|
||||
http_response_file=$workdir/http-response.txt
|
||||
|
||||
cleanup_workdir() {
|
||||
run_root_env "$herd_bin" -s "$socket_file" stop root >/dev/null 2>&1 || true
|
||||
if [ -f "$pid_file" ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
kill "$(cat "$pid_file")" >/dev/null 2>&1 || true
|
||||
else
|
||||
sudo sh -c 'kill "$(cat "$1")" >/dev/null 2>&1 || true' sh "$pid_file"
|
||||
fi
|
||||
fi
|
||||
if [ "$cleanup" -eq 1 ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
rm -rf "$workdir"
|
||||
else
|
||||
sudo rm -rf "$workdir"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
trap cleanup_workdir EXIT INT TERM
|
||||
|
||||
mkdir -p "$state_dir"
|
||||
chmod 0755 "$workdir"
|
||||
chmod 0777 "$state_dir"
|
||||
|
||||
cat >"$workdir/logger.sh" <<'EOF'
|
||||
#!/bin/sh
|
||||
log=$1
|
||||
ready=$2
|
||||
uidfile=$3
|
||||
printf 'logger-start uid=%s gid=%s\n' "$(id -u)" "$(id -g)" >> "$log"
|
||||
id -u > "$uidfile"
|
||||
touch "$ready"
|
||||
trap 'echo logger-stop >> "$log"; exit 0' TERM INT
|
||||
while :; do
|
||||
echo heartbeat >> "$log"
|
||||
sleep 1
|
||||
done
|
||||
EOF
|
||||
chmod +x "$workdir/logger.sh"
|
||||
|
||||
cat >"$workdir/web.sh" <<'EOF'
|
||||
#!/bin/sh
|
||||
port=$1
|
||||
ready=$2
|
||||
log=$3
|
||||
echo web-start >> "$log"
|
||||
touch "$ready"
|
||||
trap 'echo web-stop >> "$log"; exit 0' TERM INT
|
||||
while :; do
|
||||
printf 'HTTP/1.0 200 OK\r\nContent-Length: 20\r\n\r\nshepherd-freebsd-ok\n' | nc -l 127.0.0.1 "$port"
|
||||
done
|
||||
EOF
|
||||
chmod +x "$workdir/web.sh"
|
||||
|
||||
cat >"$workdir/monitor.sh" <<'EOF'
|
||||
#!/bin/sh
|
||||
watch=$1
|
||||
event=$2
|
||||
ready=$3
|
||||
log=$4
|
||||
echo monitor-start >> "$log"
|
||||
touch "$ready"
|
||||
trap 'echo monitor-stop >> "$log"; exit 0' TERM INT
|
||||
while :; do
|
||||
if [ -f "$watch" ] && [ ! -f "$event" ]; then
|
||||
echo detected > "$event"
|
||||
echo monitor-detected >> "$log"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
EOF
|
||||
chmod +x "$workdir/monitor.sh"
|
||||
|
||||
cat >"$workdir/crashy.sh" <<'EOF'
|
||||
#!/bin/sh
|
||||
counter=$1
|
||||
ready=$2
|
||||
log=$3
|
||||
n=0
|
||||
if [ -f "$counter" ]; then
|
||||
n=$(cat "$counter")
|
||||
fi
|
||||
n=$((n + 1))
|
||||
echo "$n" > "$counter"
|
||||
echo "crashy-attempt-$n" >> "$log"
|
||||
if [ "$n" -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
touch "$ready"
|
||||
trap 'echo crashy-stop >> "$log"; exit 0' TERM INT
|
||||
while :; do
|
||||
sleep 10
|
||||
done
|
||||
EOF
|
||||
chmod +x "$workdir/crashy.sh"
|
||||
|
||||
cat >"$config_file" <<EOF
|
||||
(use-modules (shepherd service)
|
||||
(shepherd support))
|
||||
(register-services
|
||||
(list
|
||||
(service '(logger)
|
||||
#:documentation "Write a heartbeat log as an unprivileged service."
|
||||
#:start (make-forkexec-constructor (list "$workdir/logger.sh" "$service_command_log" "$state_dir/logger.ready" "$state_dir/logger.uid")
|
||||
#:user "nobody" #:group "nobody"
|
||||
#:directory "$state_dir"
|
||||
#:log-file "$state_dir/logger.stdout")
|
||||
#:stop (make-kill-destructor)
|
||||
#:respawn? #f)
|
||||
(service '(web)
|
||||
#:documentation "Serve a tiny HTTP response over loopback."
|
||||
#:requirement '(logger)
|
||||
#:start (make-forkexec-constructor (list "$workdir/web.sh" "$port" "$state_dir/web.ready" "$service_command_log")
|
||||
#:user "nobody" #:group "nobody"
|
||||
#:directory "$state_dir"
|
||||
#:log-file "$state_dir/web.stdout")
|
||||
#:stop (make-kill-destructor)
|
||||
#:respawn? #f)
|
||||
(service '(file-monitor)
|
||||
#:documentation "Watch for a flag file and record detection."
|
||||
#:requirement '(web)
|
||||
#:start (make-forkexec-constructor (list "$workdir/monitor.sh" "$state_dir/watch.flag" "$state_dir/event.flag" "$state_dir/monitor.ready" "$service_command_log")
|
||||
#:user "nobody" #:group "nobody"
|
||||
#:directory "$state_dir"
|
||||
#:log-file "$state_dir/monitor.stdout")
|
||||
#:stop (make-kill-destructor)
|
||||
#:respawn? #f)
|
||||
(service '(crashy)
|
||||
#:documentation "Fail once and then stay running to validate respawn."
|
||||
#:start (make-forkexec-constructor (list "$workdir/crashy.sh" "$state_dir/crashy.counter" "$state_dir/crashy.ready" "$service_command_log")
|
||||
#:directory "$state_dir"
|
||||
#:log-file "$state_dir/crashy.stdout")
|
||||
#:stop (make-kill-destructor)
|
||||
#:respawn? #t)))
|
||||
EOF
|
||||
|
||||
run_root_env "$shepherd_bin" -I -s "$socket_file" -c "$config_file" --pid="$pid_file" -l "$shepherd_log" >"$shepherd_stdout" 2>&1 &
|
||||
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if [ -f "$pid_file" ] && [ -S "$socket_file" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [ ! -f "$pid_file" ] || [ ! -S "$socket_file" ]; then
|
||||
echo "Shepherd did not become ready" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" start file-monitor >/dev/null
|
||||
for ready in "$state_dir/logger.ready" "$state_dir/web.ready" "$state_dir/monitor.ready"; do
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ -f "$ready" ] && break
|
||||
sleep 1
|
||||
done
|
||||
[ -f "$ready" ] || {
|
||||
echo "Expected readiness file missing: $ready" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" status logger >"$logger_status"
|
||||
run_root_env "$herd_bin" -s "$socket_file" status web >"$web_status"
|
||||
run_root_env "$herd_bin" -s "$socket_file" status file-monitor >"$monitor_status"
|
||||
|
||||
fetch -T 5 -qo "$http_response_file" "http://127.0.0.1:$port/"
|
||||
http_response=$(tr -d '\r' <"$http_response_file")
|
||||
if [ "$http_response" != "shepherd-freebsd-ok" ]; then
|
||||
echo "Unexpected HTTP response: $http_response" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch "$state_dir/watch.flag"
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ -f "$state_dir/event.flag" ] && break
|
||||
sleep 1
|
||||
done
|
||||
[ -f "$state_dir/event.flag" ] || {
|
||||
echo "Monitor did not detect watched file" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" start crashy >/dev/null || true
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ -f "$state_dir/crashy.ready" ] && break
|
||||
sleep 1
|
||||
done
|
||||
[ -f "$state_dir/crashy.ready" ] || {
|
||||
echo "Respawn test service did not reach steady state" >&2
|
||||
exit 1
|
||||
}
|
||||
run_root_env "$herd_bin" -s "$socket_file" status crashy >"$crashy_status"
|
||||
|
||||
logger_uid=$(cat "$state_dir/logger.uid")
|
||||
crashy_counter=$(cat "$state_dir/crashy.counter")
|
||||
if [ "$logger_uid" != "65534" ]; then
|
||||
echo "Expected logger to run as nobody (65534), got $logger_uid" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$crashy_counter" -lt 2 ]; then
|
||||
echo "Expected respawn counter >= 2, got $crashy_counter" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" stop root >/dev/null || true
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ ! -f "$pid_file" ] && break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
case $(cat "$logger_status") in
|
||||
*"It is running"*) logger_running=yes ;;
|
||||
*) logger_running=no ;;
|
||||
esac
|
||||
case $(cat "$web_status") in
|
||||
*"It is running"*) web_running=yes ;;
|
||||
*) web_running=no ;;
|
||||
esac
|
||||
case $(cat "$monitor_status") in
|
||||
*"It is running"*) monitor_running=yes ;;
|
||||
*) monitor_running=no ;;
|
||||
esac
|
||||
case $(cat "$crashy_status") in
|
||||
*"It is running"*) crashy_running=yes ;;
|
||||
*) crashy_running=no ;;
|
||||
esac
|
||||
case $(cat "$shepherd_stdout") in
|
||||
*"System lacks support for 'signalfd'; using fallback mechanism."*) signalfd_fallback=yes ;;
|
||||
*) signalfd_fallback=no ;;
|
||||
esac
|
||||
|
||||
cat >"$metadata_file" <<EOF
|
||||
workdir=$workdir
|
||||
shepherd_prefix=$shepherd_prefix
|
||||
guile_bin=$guile_bin
|
||||
guile_extra_prefix=$guile_extra_prefix
|
||||
port=$port
|
||||
socket_file=$socket_file
|
||||
pid_file=$pid_file
|
||||
shepherd_log=$shepherd_log
|
||||
shepherd_stdout=$shepherd_stdout
|
||||
service_command_log=$service_command_log
|
||||
logger_status_file=$logger_status
|
||||
web_status_file=$web_status
|
||||
monitor_status_file=$monitor_status
|
||||
crashy_status_file=$crashy_status
|
||||
logger_running=$logger_running
|
||||
web_running=$web_running
|
||||
monitor_running=$monitor_running
|
||||
crashy_running=$crashy_running
|
||||
logger_uid=$logger_uid
|
||||
http_response=$http_response
|
||||
monitor_detected=$(cat "$state_dir/event.flag")
|
||||
crashy_counter=$crashy_counter
|
||||
signalfd_fallback=$signalfd_fallback
|
||||
EOF
|
||||
|
||||
if [ -n "$metadata_target" ]; then
|
||||
mkdir -p "$(dirname "$metadata_target")"
|
||||
cp "$metadata_file" "$metadata_target"
|
||||
fi
|
||||
|
||||
printf 'PASS freebsd-shepherd-service-prototype\n'
|
||||
printf 'Work directory: %s\n' "$workdir"
|
||||
printf 'Metadata file: %s\n' "$metadata_file"
|
||||
if [ -n "$metadata_target" ]; then
|
||||
printf 'Copied metadata to: %s\n' "$metadata_target"
|
||||
fi
|
||||
printf '%s\n' '--- metadata ---'
|
||||
cat "$metadata_file"
|
||||
Reference in New Issue
Block a user