Spaces:
Running
Running
| # 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 | |