Rename the local deterministic launch helper from colibri-smoke-agent to colibri-test-agent, update CLI/TUI/tests/docs, and teach the FreeBSD rc.d service to source /usr/local/etc/colibri/provider.env plus set a service PATH for local spawns.\n\nChecks: cargo fmt --check; ./scripts/check-format.sh; git diff --check; cargo check -p colibri-daemon -p colibri-client -p colibri-glasspane-tui; cargo check -p colibri-client --bins; cargo test -p colibri-client --test live_socket_check -- --nocapture.
187 lines
6.8 KiB
Bash
187 lines
6.8 KiB
Bash
#!/bin/sh
|
|
#
|
|
# Colibri daemon — FreeBSD rc.d service
|
|
#
|
|
# colibri-daemon runs in the FOREGROUND — it does not self-daemonize or write a
|
|
# pidfile. rc.d runs it under daemon(8), which backgrounds it, writes the
|
|
# child pidfile (colibri-daemon PID), restarts on crash, and redirects
|
|
# stdout/stderr (tracing) to a logfile. rc.subr performs the privilege drop
|
|
# through ${name}_user.
|
|
#
|
|
# Setup (one-time, as root):
|
|
# pw groupadd colibri
|
|
# pw useradd colibri -g colibri -d /var/db/colibri -s /usr/sbin/nologin
|
|
# cp packaging/freebsd/colibri_daemon.in /usr/local/etc/rc.d/colibri_daemon
|
|
# chmod 555 /usr/local/etc/rc.d/colibri_daemon
|
|
# sysrc colibri_daemon_enable=YES # or NO during dual-run
|
|
#
|
|
# Runtime:
|
|
# service colibri_daemon start
|
|
# service colibri_daemon status
|
|
# service colibri_daemon stop
|
|
#
|
|
# Requires:
|
|
# - colibri-daemon binary at /usr/local/bin/colibri-daemon
|
|
# - colibri user/group (privilege drop target)
|
|
|
|
# PROVIDE: colibri_daemon
|
|
# REQUIRE: LOGIN cleanvar
|
|
# KEYWORD: shutdown
|
|
|
|
. /etc/rc.subr
|
|
|
|
name="colibri_daemon"
|
|
rcvar="colibri_daemon_enable"
|
|
|
|
load_rc_config $name
|
|
|
|
: ${colibri_daemon_enable:="NO"}
|
|
: ${colibri_daemon_user:="colibri"}
|
|
: ${colibri_daemon_group:="colibri"}
|
|
: ${colibri_daemon_binary:="/usr/local/bin/colibri-daemon"}
|
|
: ${colibri_daemon_data_dir:="/var/db/colibri"}
|
|
: ${colibri_daemon_run_dir:="/var/run/colibri"}
|
|
: ${colibri_daemon_socket:="${colibri_daemon_run_dir}/colibri.sock"}
|
|
: ${colibri_daemon_db_path:="${colibri_daemon_data_dir}/colibri.sqlite"}
|
|
: ${colibri_daemon_logfile:="/var/log/colibri/daemon.log"}
|
|
: ${colibri_daemon_provider_env:="/usr/local/etc/colibri/provider.env"}
|
|
: ${colibri_daemon_host:="$(/bin/hostname)"}
|
|
: ${colibri_cost_mode:="smart"}
|
|
|
|
pidfile="${colibri_daemon_run_dir}/colibri-daemon.pid"
|
|
# Supervisor pidfile (the daemon(8) parent). Kept distinct from the child
|
|
# pidfile so `stop` can target the supervisor — see colibri_daemon_stop.
|
|
supervisor_pidfile="${colibri_daemon_run_dir}/colibri-daemon-supervisor.pid"
|
|
|
|
# Run colibri-daemon under daemon(8):
|
|
# -P supervisor pidfile (the daemon(8) parent — used by stop)
|
|
# -p child pidfile (writes colibri-daemon PID — used by start/status)
|
|
# -r restart on crash, -t process title,
|
|
# -o append stdout/stderr to log.
|
|
# rc.subr already runs the command as ${colibri_daemon_user}; do not also pass
|
|
# daemon(8) -u or daemon(8) will try to drop privileges a second time.
|
|
command="/usr/sbin/daemon"
|
|
command_args="-P ${supervisor_pidfile} -p ${pidfile} -r -t ${name} \
|
|
-o ${colibri_daemon_logfile} ${colibri_daemon_binary}"
|
|
|
|
# Use the child's process name so rc.subr can find the right process via the
|
|
# child pidfile. Using the daemon(8) supervisor path would collide with
|
|
# tailscaled and other daemon(8)-managed services on the system.
|
|
procname="colibri-daemon"
|
|
|
|
start_precmd="colibri_daemon_prestart"
|
|
start_postcmd="colibri_daemon_poststart"
|
|
stop_cmd="colibri_daemon_stop"
|
|
stop_postcmd="colibri_daemon_poststop"
|
|
extra_commands="health"
|
|
|
|
colibri_daemon_prestart()
|
|
{
|
|
# /var/run is tmpfs on FreeBSD (wiped each boot) — recreate every start.
|
|
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
|
|
"${colibri_daemon_run_dir}"
|
|
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
|
|
"${colibri_daemon_data_dir}"
|
|
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
|
|
"$(/usr/bin/dirname "${colibri_daemon_logfile}")"
|
|
|
|
# Remove stale runtime files while rc.d is still root. The daemon process
|
|
# runs as colibri and cannot unlink a stale socket left behind by a prior
|
|
# root/corrupt manual start.
|
|
rm -f "${colibri_daemon_socket}" "${pidfile}" "${supervisor_pidfile}"
|
|
|
|
# Provider keys are optional. Keep them in a root-owned env file instead of
|
|
# rc.conf so they are easy to rotate and not world-readable.
|
|
if [ -r "${colibri_daemon_provider_env}" ]; then
|
|
set -a
|
|
. "${colibri_daemon_provider_env}"
|
|
set +a
|
|
fi
|
|
|
|
# Config is passed to the child via the environment.
|
|
export COLIBRI_DAEMON_DATA_DIR="${colibri_daemon_data_dir}"
|
|
export COLIBRI_DAEMON_SOCKET="${colibri_daemon_socket}"
|
|
export COLIBRI_DB_PATH="${colibri_daemon_db_path}"
|
|
export COLIBRI_HOST="${colibri_daemon_host}"
|
|
export COLIBRI_COST_MODE="${colibri_cost_mode}"
|
|
}
|
|
|
|
colibri_daemon_poststart()
|
|
{
|
|
# Wait for the socket to appear (daemon forks, child binds socket).
|
|
local timeout=10
|
|
local waited=0
|
|
while [ ! -S "${colibri_daemon_socket}" ] && [ $waited -lt $timeout ]; do
|
|
sleep 1
|
|
waited=$((waited + 1))
|
|
done
|
|
|
|
if [ -S "${colibri_daemon_socket}" ]; then
|
|
echo "colibri-daemon socket ready after ${waited}s"
|
|
chmod 644 "${pidfile}" 2>/dev/null || true
|
|
chmod 660 "${colibri_daemon_socket}" 2>/dev/null || true
|
|
else
|
|
echo "WARNING: colibri-daemon socket not ready after ${timeout}s"
|
|
fi
|
|
}
|
|
|
|
colibri_daemon_stop()
|
|
{
|
|
# daemon(8) -r restarts the child if it is killed directly, so a plain
|
|
# SIGTERM to the child pidfile would just be undone. Stop the supervisor
|
|
# instead: on SIGTERM it forwards the signal to the child and exits without
|
|
# restarting it.
|
|
local _sup=""
|
|
[ -f "${supervisor_pidfile}" ] && _sup=$(cat "${supervisor_pidfile}" 2>/dev/null)
|
|
if [ -n "${_sup}" ] && kill -0 "${_sup}" 2>/dev/null; then
|
|
echo "Stopping ${name} (daemon(8) supervisor pid ${_sup})."
|
|
kill -TERM "${_sup}" 2>/dev/null
|
|
local _n=0
|
|
while kill -0 "${_sup}" 2>/dev/null && [ ${_n} -lt 30 ]; do
|
|
sleep 1
|
|
_n=$((_n + 1))
|
|
done
|
|
if kill -0 "${_sup}" 2>/dev/null; then
|
|
echo "Supervisor did not exit in time; sending SIGKILL."
|
|
kill -KILL "${_sup}" 2>/dev/null
|
|
fi
|
|
else
|
|
echo "${name} is not running."
|
|
fi
|
|
# Belt-and-suspenders: terminate the child if it somehow outlived the
|
|
# supervisor (e.g. supervisor SIGKILLed before it could clean up).
|
|
local _ch=""
|
|
[ -f "${pidfile}" ] && _ch=$(cat "${pidfile}" 2>/dev/null)
|
|
if [ -n "${_ch}" ] && kill -0 "${_ch}" 2>/dev/null; then
|
|
kill -TERM "${_ch}" 2>/dev/null
|
|
fi
|
|
rm -f "${supervisor_pidfile}" "${pidfile}"
|
|
}
|
|
|
|
colibri_daemon_poststop()
|
|
{
|
|
# Clean up tmpfs artifacts on graceful shutdown.
|
|
# The pidfile is managed by daemon(8); socket is the child's.
|
|
if [ -S "${colibri_daemon_socket}" ]; then
|
|
rm -f "${colibri_daemon_socket}"
|
|
fi
|
|
}
|
|
|
|
health_cmd="colibri_daemon_health"
|
|
colibri_daemon_health()
|
|
{
|
|
if [ -S "${colibri_daemon_socket}" ]; then
|
|
if printf '{"cmd":"status"}\n' | nc -U "${colibri_daemon_socket}" -w 2 >/dev/null 2>&1; then
|
|
echo "colibri-daemon is healthy (socket responding)"
|
|
return 0
|
|
else
|
|
echo "colibri-daemon socket exists but not responding"
|
|
return 1
|
|
fi
|
|
else
|
|
echo "colibri-daemon socket not found"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
run_rc_command "$1"
|