80f7322294
"Nothing on the host" meant two different things and the script treated them the same. If docker answers and names no running containers, Maven really is down and the script should say so and exit 0. Only when docker cannot be asked is the answer unknown, and that is the case that must fail loudly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
119 lines
4.7 KiB
Bash
Executable File
119 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Unified script to stop all Maven services.
|
|
# Usage: ./kill-maven.sh
|
|
# - Docker deploy: `docker compose stop` (see why below).
|
|
# - Bare-metal / dev run: graceful SIGTERM first, SIGKILL if anything lingers.
|
|
# Exits non-zero if it cannot confirm everything is stopped. It must never say
|
|
# "stopped" unless it checked.
|
|
|
|
set -euo pipefail
|
|
|
|
# The llama-server the phraser spawns has NO "maven" in its command line (its
|
|
# args are `-m /path/to/<model>.gguf --port ...`), so a `llama-server.*maven`
|
|
# pattern matches nothing and leaks it — the exact bug that let orphans pile up
|
|
# and OOM the box.
|
|
#
|
|
# We used to match the model name, defaulting to LFM2. The deploy now runs
|
|
# Qwen3.5-0.8B, so that default matched nothing and the server survived every
|
|
# kill. Match any llama-server serving a .gguf instead, so swapping the model in
|
|
# deploy/mavend.json cannot break this script again. Set MODEL to narrow it if
|
|
# some other llama-server on this box must be left alone.
|
|
MODEL="${MODEL:-}"
|
|
PAT='mavend|mavsttd|mavttsd|mavweb|mavpoll|mavenclient'
|
|
if [ -n "$MODEL" ]; then
|
|
LLM="llama-server.*${MODEL}"
|
|
else
|
|
LLM='llama-server.*\.gguf'
|
|
fi
|
|
|
|
COMPOSE_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/docker-compose.yml"
|
|
|
|
# --- containerised deploy ------------------------------------------------
|
|
# docker-compose.yml does not set `pid: host`, so each container has its own
|
|
# PID namespace: pkill on the host sees nothing inside them. This script used
|
|
# to print "all stopped" while every daemon was still happily running. Stop the
|
|
# containers through compose instead — that actually reaches them.
|
|
#
|
|
# running_containers prints the ids of the project's running containers, or
|
|
# nothing. Empty output plus a non-zero return means "could not ask docker",
|
|
# which is different from "nothing is running" and is handled below.
|
|
running_containers() {
|
|
docker compose -f "$COMPOSE_FILE" ps -q --status running 2>/dev/null
|
|
}
|
|
|
|
DOCKER_OK=0
|
|
CONTAINERS=""
|
|
if command -v docker >/dev/null 2>&1 && [ -f "$COMPOSE_FILE" ]; then
|
|
if CONTAINERS="$(running_containers)"; then
|
|
DOCKER_OK=1
|
|
fi
|
|
fi
|
|
|
|
if [ "$DOCKER_OK" = 1 ] && [ -n "$CONTAINERS" ]; then
|
|
echo "--- Maven is running in containers: stopping via docker compose ---"
|
|
if ! docker compose -f "$COMPOSE_FILE" stop; then
|
|
echo "ERROR: 'docker compose stop' failed. Containers may still be running." >&2
|
|
exit 1
|
|
fi
|
|
echo "--- Verifying containers are gone ---"
|
|
LEFT="$(running_containers || true)"
|
|
if [ -n "$LEFT" ]; then
|
|
echo "ERROR: containers still running after stop:" >&2
|
|
docker compose -f "$COMPOSE_FILE" ps >&2 || true
|
|
exit 1
|
|
fi
|
|
echo "All containers stopped."
|
|
exit 0
|
|
fi
|
|
|
|
# --- bare-metal / dev run -----------------------------------------------
|
|
# pgrep -f matches whole command lines, so a shell that merely mentions
|
|
# "mavend" (this script's own parent, for one) shows up. Drop ourselves and our
|
|
# parent, otherwise the SIGKILL sweep can take out the terminal you ran this in.
|
|
host_pids() {
|
|
pgrep -f "$PAT|$LLM" | grep -v -e "^$$\$" -e "^$PPID\$" | paste -sd, - || true
|
|
}
|
|
HOST_PIDS=$(host_pids)
|
|
|
|
if [ -z "$HOST_PIDS" ]; then
|
|
# Nothing on the host. Whether that means "already down" depends on whether
|
|
# we managed to ask docker, and the two must not read the same.
|
|
if [ "$DOCKER_OK" = 1 ]; then
|
|
# Docker answered and named no running containers, and there is nothing
|
|
# on the host either. That is a real answer: Maven is already stopped.
|
|
echo "Nothing to stop: no Maven processes and no running containers."
|
|
exit 0
|
|
fi
|
|
# We could not ask docker, so Maven may be alive in a container we cannot
|
|
# see. Saying "stopped" here is the exact false success this script had.
|
|
echo "ERROR: no Maven processes on this host, and docker could not be asked." >&2
|
|
echo " If this is the container deploy it may still be running:" >&2
|
|
echo " docker compose -f $COMPOSE_FILE stop" >&2
|
|
echo " Nothing was stopped. Check by hand before assuming Maven is down." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "--- Sending graceful SIGTERM to Maven services ---"
|
|
pkill -TERM -f "$PAT" || true
|
|
# mavend's Pdeathsig SIGKILLs its llama-server on exit, but sweep strays too
|
|
# (an orphan whose mavend already died has no one left to reap it).
|
|
pkill -TERM -f "$LLM" || true
|
|
|
|
echo "--- Verifying processes are gone ---"
|
|
sleep 1
|
|
PIDS=$(host_pids)
|
|
if [ -n "$PIDS" ]; then
|
|
echo "Warning: some processes still alive. PIDs: $PIDS"
|
|
echo "--- Force killing with SIGKILL ---"
|
|
echo "$PIDS" | tr ',' '\n' | xargs -r kill -9
|
|
sleep 1
|
|
LEFT=$(host_pids)
|
|
if [ -n "$LEFT" ]; then
|
|
echo "ERROR: still alive after SIGKILL. PIDs: $LEFT" >&2
|
|
exit 1
|
|
fi
|
|
echo "Done (SIGKILL)."
|
|
else
|
|
echo "All services gracefully stopped."
|
|
fi
|