Files
fruix/tests/shepherd/run-freebsd-shepherd-init-prototype.sh

297 lines
8.9 KiB
Bash
Executable File

#!/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
run_root() {
if [ "$(id -u)" -eq 0 ]; then
"$@"
else
sudo "$@"
fi
}
cleanup=0
if [ -n "${WORKDIR:-}" ]; then
workdir=$WORKDIR
mkdir -p "$workdir"
else
workdir=$(mktemp -d /tmp/freebsd-shepherd-init.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
chmod 0755 "$workdir"
config_file=$workdir/init-config.scm
boot_log=$workdir/boot-order.log
pid_file=$workdir/shepherd.pid
socket_file=$workdir/shepherd.sock
shepherd_log=$workdir/shepherd.log
shepherd_stdout=$workdir/shepherd.out
metadata_file=$workdir/freebsd-shepherd-init-metadata.txt
rc_name=fruix_shepherd_boot_$$
rc_script=/usr/local/etc/rc.d/$rc_name
rc_template=$workdir/rc-template.in
cleanup_workdir() {
run_root service "$rc_name" onestop >/dev/null 2>&1 || true
run_root rm -f "$rc_script"
if [ "$cleanup" -eq 1 ]; then
run_root rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
cat >"$config_file" <<EOF
(use-modules (shepherd service)
(shepherd support))
(define (append-line text)
(let ((port (open-file "$boot_log" "a")))
(display text port)
(newline port)
(close-port port)))
(register-services
(list
(service '(filesystems)
#:documentation "Prototype filesystem initialization target."
#:start (lambda _
(append-line "start:filesystems")
(call-with-output-file "$workdir/filesystems.ready"
(lambda (port) (display "ok" port)))
#t)
#:stop (lambda _
(append-line "stop:filesystems")
#f)
#:respawn? #f)
(service '(system-log)
#:documentation "Prototype logging target."
#:requirement '(filesystems)
#:start (lambda _
(append-line "start:system-log")
(call-with-output-file "$workdir/system-log.ready"
(lambda (port) (display "ok" port)))
#t)
#:stop (lambda _
(append-line "stop:system-log")
#f)
#:respawn? #f)
(service '(networking)
#:documentation "Prototype networking target."
#:requirement '(system-log)
#:start (lambda _
(append-line "start:networking")
(call-with-output-file "$workdir/networking.ready"
(lambda (port) (display "ok" port)))
#t)
#:stop (lambda _
(append-line "stop:networking")
#f)
#:respawn? #f)
(service '(login)
#:documentation "Prototype login target."
#:requirement '(networking)
#:start (lambda _
(append-line "start:login")
(call-with-output-file "$workdir/login.ready"
(lambda (port) (display "ok" port)))
#t)
#:stop (lambda _
(append-line "stop:login")
#f)
#:respawn? #f)))
(start-service (lookup-service 'login))
EOF
cat >"$rc_template" <<'EOF'
#!/bin/sh
# PROVIDE: __RC_NAME__
# REQUIRE: LOGIN
# KEYWORD: shutdown
. /etc/rc.subr
name=__RC_NAME__
rcvar="${name}_enable"
: ${__RC_ENABLE_VAR__:=YES}
pidfile=__PIDFILE__
socket=__SOCKET__
config=__CONFIG__
logfile=__LOGFILE__
command=__SHEPHERD_BIN__
start_cmd="__RC_START_FN__"
stop_cmd="__RC_STOP_FN__"
status_cmd="__RC_STATUS_FN__"
__RC_START_FN__()
{
env LD_LIBRARY_PATH=__LD_LIBRARY_PATH__ \
GUILE_LOAD_PATH=__GUILE_LOAD_PATH__ \
GUILE_LOAD_COMPILED_PATH=__GUILE_LOAD_COMPILED_PATH__ \
GUILE_EXTENSIONS_PATH=__GUILE_EXTENSIONS_PATH__ \
__SHEPHERD_BIN__ -I -s "$socket" -c "$config" --pid="$pidfile" -l "$logfile" > "__STDOUT__" 2>&1 &
for _try in 1 2 3 4 5 6 7 8 9 10; do
[ -f "$pidfile" ] && [ -S "$socket" ] && return 0
sleep 1
done
return 1
}
__RC_STOP_FN__()
{
env LD_LIBRARY_PATH=__LD_LIBRARY_PATH__ \
GUILE_LOAD_PATH=__GUILE_LOAD_PATH__ \
GUILE_LOAD_COMPILED_PATH=__GUILE_LOAD_COMPILED_PATH__ \
GUILE_EXTENSIONS_PATH=__GUILE_EXTENSIONS_PATH__ \
__HERD_BIN__ -s "$socket" stop root >/dev/null 2>&1 || true
for _try in 1 2 3 4 5 6 7 8 9 10; do
[ ! -f "$pidfile" ] && return 0
sleep 1
done
kill "$(cat "$pidfile")" >/dev/null 2>&1 || true
rm -f "$pidfile"
return 0
}
__RC_STATUS_FN__()
{
[ -f "$pidfile" ] && kill -0 "$(cat "$pidfile")" >/dev/null 2>&1
}
load_rc_config $name
run_rc_command "$1"
EOF
rc_start_fn=${rc_name}_start
rc_stop_fn=${rc_name}_stop
rc_status_fn=${rc_name}_status
rc_enable_var=${rc_name}_enable
guile_bindir=$(CDPATH= cd -- "$(dirname "$guile_bin")" && pwd)
guile_prefix=$(CDPATH= cd -- "$guile_bindir/.." && pwd)
ld_library_path=$guile_extra_prefix/lib:$guile_prefix/lib:/usr/local/lib
guile_load_path=$guile_extra_prefix/share/guile/site/3.0
guile_load_compiled_path=$guile_extra_prefix/lib/guile/3.0/site-ccache
guile_extensions_path=$guile_extra_prefix/lib/guile/3.0/extensions
sed \
-e "s|__RC_NAME__|$rc_name|g" \
-e "s|__RC_ENABLE_VAR__|$rc_enable_var|g" \
-e "s|__RC_START_FN__|$rc_start_fn|g" \
-e "s|__RC_STOP_FN__|$rc_stop_fn|g" \
-e "s|__RC_STATUS_FN__|$rc_status_fn|g" \
-e "s|__PIDFILE__|$pid_file|g" \
-e "s|__SOCKET__|$socket_file|g" \
-e "s|__CONFIG__|$config_file|g" \
-e "s|__LOGFILE__|$shepherd_log|g" \
-e "s|__STDOUT__|$shepherd_stdout|g" \
-e "s|__SHEPHERD_BIN__|$shepherd_bin|g" \
-e "s|__HERD_BIN__|$herd_bin|g" \
-e "s|__LD_LIBRARY_PATH__|$ld_library_path|g" \
-e "s|__GUILE_LOAD_PATH__|$guile_load_path|g" \
-e "s|__GUILE_LOAD_COMPILED_PATH__|$guile_load_compiled_path|g" \
-e "s|__GUILE_EXTENSIONS_PATH__|$guile_extensions_path|g" \
"$rc_template" | run_root tee "$rc_script" >/dev/null
run_root chmod +x "$rc_script"
run_root service "$rc_name" onestart
for ready in filesystems.ready system-log.ready networking.ready login.ready; do
[ -f "$workdir/$ready" ] || {
echo "Expected boot marker missing: $workdir/$ready" >&2
exit 1
}
done
if run_root service "$rc_name" onestatus >/dev/null 2>&1; then
rc_status=running
else
rc_status=stopped
fi
start_sequence=$(paste -sd, "$boot_log")
expected_start_sequence=start:filesystems,start:system-log,start:networking,start:login
if [ "$start_sequence" != "$expected_start_sequence" ]; then
echo "Unexpected boot sequence: $start_sequence" >&2
exit 1
fi
run_root service "$rc_name" onestop
stop_sequence=$(tail -n 4 "$boot_log" | paste -sd, -)
expected_stop_sequence=stop:login,stop:networking,stop:system-log,stop:filesystems
if [ "$stop_sequence" != "$expected_stop_sequence" ]; then
echo "Unexpected shutdown sequence: $stop_sequence" >&2
exit 1
fi
if [ -f "$pid_file" ]; then
echo "PID file still present after stop: $pid_file" >&2
exit 1
fi
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
rc_name=$rc_name
rc_script=$rc_script
config_file=$config_file
boot_log=$boot_log
pid_file=$pid_file
socket_file=$socket_file
shepherd_log=$shepherd_log
shepherd_stdout=$shepherd_stdout
rc_status=$rc_status
start_sequence=$start_sequence
expected_start_sequence=$expected_start_sequence
stop_sequence=$stop_sequence
expected_stop_sequence=$expected_stop_sequence
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-init-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"