marionette-js / tests /scripts /daemon.sh
RemiFabre
Add daemon control script with SIGINT shutdown
6f407fd
Raw
History Blame Contribute Delete
4.56 kB
#!/usr/bin/env bash
# Start / stop the local reachy-mini daemon cleanly for tests.
#
# WHY: pkill -9 leaks GStreamer semaphores and skips the daemon's
# shutdown procedure (sleep pose, mDNS unregister, peer connection
# teardown). That can leave the local WebRTC pipe in a broken state
# until the laptop reboots β€” observed 2026-05-06. SIGTERM lets the
# daemon run its proper shutdown.
#
# Usage:
# tests/scripts/daemon.sh start # start, wait for "Daemon started successfully"
# tests/scripts/daemon.sh stop # SIGTERM, wait for clean exit
# tests/scripts/daemon.sh restart # stop, then start
set -euo pipefail
LOG=/tmp/reachy-daemon.log
VENV="${VENV:-$HOME/.virtualenvs/mini}"
START_TIMEOUT=60
STOP_TIMEOUT=30
cmd="${1:-}"
start() {
if pgrep -f reachy-mini-daemon >/dev/null; then
echo "daemon already running (PID $(pgrep -f reachy-mini-daemon | head -1))"
return 0
fi
# Append (not truncate) so prior sessions' shutdown logs survive for
# debugging. A separator marks the boundary.
{
echo ""
echo "=== daemon start at $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
} >> "$LOG"
# shellcheck disable=SC1091
source "$VENV/bin/activate"
nohup reachy-mini-daemon >> "$LOG" 2>&1 &
disown
local pid=$!
echo "daemon starting (PID $pid)"
local deadline=$(( $(date +%s) + START_TIMEOUT ))
# Look ONLY at the lines after the most recent "=== daemon start at"
# marker. Reset matched=0 on each new marker so we don't trigger on a
# success line from a previous session that's still in the log.
until awk '/=== daemon start at /{found=1; matched=0} found && /Daemon started successfully/{matched=1} END{exit !matched}' "$LOG" 2>/dev/null; do
if ! kill -0 "$pid" 2>/dev/null; then
echo "daemon died during startup; tail of log:" >&2
tail -20 "$LOG" >&2
return 1
fi
if [ "$(date +%s)" -gt "$deadline" ]; then
echo "daemon failed to come up in ${START_TIMEOUT}s" >&2
tail -20 "$LOG" >&2
return 1
fi
sleep 1
done
echo "daemon ready"
}
stop() {
local pid
pid=$(pgrep -f reachy-mini-daemon | head -1 || true)
if [ -z "$pid" ]; then
echo "no daemon to stop"
return 0
fi
# SIGINT, not SIGTERM. The daemon's full shutdown procedure (put
# Reachy Mini to sleep, tear down GStreamer pipelines, unregister
# from the central signaling) is wired to KeyboardInterrupt, which
# SIGINT raises and SIGTERM does not always trigger reliably across
# Python versions and entry-point wrappers. Behaves identically to
# CTRL-C in a foreground shell.
echo "sending SIGINT to $pid"
kill -INT "$pid"
local deadline=$(( $(date +%s) + STOP_TIMEOUT ))
while kill -0 "$pid" 2>/dev/null; do
if [ "$(date +%s)" -gt "$deadline" ]; then
# Last resort. Document the failure loud and proud β€” if we
# ever fall through to SIGKILL, semaphores leak and the
# local WebRTC pipe state is suspect.
echo "daemon did not exit in ${STOP_TIMEOUT}s after SIGINT; sending SIGKILL (will leak GST resources)" >&2
kill -KILL "$pid"
return 1
fi
sleep 1
done
# Verify the FULL shutdown sequence ran. The daemon walks through:
# "Putting Reachy Mini to sleep..." ← motors safe-pose
# "Daemon stopped successfully." ← clean teardown
# Both should appear after the latest start marker. If only the
# second is present, something in the sleep procedure failed and
# the next session may inherit a half-broken state.
local sleep_line=0 stopped_line=0
if awk '/=== daemon start at /{f=1; sleep_seen=0; stopped_seen=0} f && /Putting Reachy Mini to sleep/{sleep_seen=1} f && /Daemon stopped successfully/{stopped_seen=1} END{exit !(sleep_seen && stopped_seen)}' "$LOG" 2>/dev/null; then
echo "daemon stopped cleanly (sleep pose + full teardown ran)"
elif awk '/=== daemon start at /{f=1; m=0} f && /Daemon stopped successfully/{m=1} END{exit !m}' "$LOG" 2>/dev/null; then
echo "WARN: daemon exited but did NOT run sleep pose; next session may be unhealthy" >&2
tail -15 "$LOG" >&2
else
echo "WARN: daemon exited without clean-shutdown markers in log; tail:" >&2
tail -15 "$LOG" >&2
fi
}
case "$cmd" in
start) start ;;
stop) stop ;;
restart) stop && start ;;
*) echo "usage: $0 {start|stop|restart}" >&2; exit 2 ;;
esac