← Files MOOS-IvP SkillsARCHIVED FILE
skills/moos-ivp-harness-builder/assets/moos_scoped_teardown.sh
8.05 KB · Oct 5, 2026 · 18:35 UTC
#!/bin/bash
#------------------------------------------------------------
# Script: moos_scoped_teardown.sh
# Author: Charles Benjamin
#------------------------------------------------------------
# Shared teardown helpers for scoped MOOS mission runs.
#
# Source this file from a mission or harness wrapper and call:
#
# moos_scoped_teardown_stop_root /path/to/mission-or-run-root
#
# The helper only targets known MOOS apps whose current working directory is
# the supplied root or a descendant of that root.
MOOS_SCOPED_TEARDOWN_GRACE_INT_SECONDS="${MOOS_SCOPED_TEARDOWN_GRACE_INT_SECONDS:-3}"
MOOS_SCOPED_TEARDOWN_GRACE_TERM_SECONDS="${MOOS_SCOPED_TEARDOWN_GRACE_TERM_SECONDS:-3}"
MOOS_SCOPED_TEARDOWN_GRACE_KILL_SECONDS="${MOOS_SCOPED_TEARDOWN_GRACE_KILL_SECONDS:-1}"
MOOS_SCOPED_TEARDOWN_SLEEP="${MOOS_SCOPED_TEARDOWN_SLEEP:-0.125}"
MOOS_SCOPED_TEARDOWN_QUIET="${MOOS_SCOPED_TEARDOWN_QUIET:-yes}"
MOOS_SCOPED_TEARDOWN_EXTRA_APPS="${MOOS_SCOPED_TEARDOWN_EXTRA_APPS:-}"
moos_scoped_teardown_log() {
if [ "$MOOS_SCOPED_TEARDOWN_QUIET" != "yes" ]; then
echo "moos_scoped_teardown: $*"
fi
}
moos_scoped_teardown_apps_for_root() {
local root="$1"
local defaults
defaults="pAntler MOOSDB pRealm pLogger uProcessWatch pShare pHostInfo"
defaults="$defaults uFldShoreBroker uFldNodeComms uTimerScript pMissionEval"
defaults="$defaults pAutoPoke pMarineViewer pMissionHash uMayFinish"
defaults="$defaults uFldNodeBroker pHelmIvP"
defaults="$defaults pMarinePID pMarinePIDV22 pNodeReporter pNodeRepo uLoadWatch"
defaults="$defaults uSimMarine uSimMarineV22"
defaults="$defaults pTrafficManager pTrafficM"
{
if [ -d "$root" ]; then
find "$root" -maxdepth 3 -type f \
\( -name 'meta_*.moos' -o -name 'meta_*.moosx' -o -name 'targ_*.moos' \) \
-print 2>/dev/null | while IFS= read -r moos_file; do
sed -n 's/^[[:space:]]*Run[[:space:]]*=[[:space:]]*\([^[:space:]@]*\).*/\1/p' "$moos_file"
done
fi
echo "$defaults"
echo "$MOOS_SCOPED_TEARDOWN_EXTRA_APPS"
} | tr ' ' '\n' | sed '/^$/d' | sort -u
}
moos_scoped_teardown_app_match() {
local command="$1"
local apps="$2"
[ "$command" != "" ] || return 1
case "$apps" in
*" $command "*) return 0 ;;
esac
return 1
}
moos_scoped_teardown_path_in_root() {
local path="$1"
local root="$2"
[ "$path" = "$root" ] || [ "${path#"$root"/}" != "$path" ]
}
moos_scoped_teardown_pids_for_root_procfs() {
local root="$1"
local apps
local proc_dir
local pid
local cwd
local command
local exe
local argv0
[ -d /proc ] || return 1
[ -L "/proc/$$/cwd" ] || return 1
readlink "/proc/$$/cwd" >/dev/null 2>&1 || return 1
root=$(cd "$root" 2>/dev/null && pwd -P)
[ "$root" != "" ] || return 1
apps=$(moos_scoped_teardown_apps_for_root "$root" | tr '\n' ' ')
for proc_dir in /proc/[0-9]*; do
[ -d "$proc_dir" ] || continue
pid="${proc_dir##*/}"
cwd=$(readlink "$proc_dir/cwd" 2>/dev/null) || continue
moos_scoped_teardown_path_in_root "$cwd" "$root" || continue
command=""
exe=$(readlink "$proc_dir/exe" 2>/dev/null) || exe=""
if [ "$exe" != "" ]; then
command="${exe##*/}"
fi
if ! moos_scoped_teardown_app_match "$command" " $apps "; then
argv0=$(tr '\000' '\n' < "$proc_dir/cmdline" 2>/dev/null | sed -n '1p') || argv0=""
if [ "$argv0" != "" ]; then
command="${argv0##*/}"
fi
fi
if ! moos_scoped_teardown_app_match "$command" " $apps "; then
command=$(sed -n '1p' "$proc_dir/comm" 2>/dev/null) || command=""
fi
if moos_scoped_teardown_app_match "$command" " $apps "; then
echo "$pid"
fi
done | sort -nu
}
moos_scoped_teardown_pids_for_root_lsof() {
local root="$1"
local apps
local listing
command -v lsof >/dev/null 2>&1 || return 1
root=$(cd "$root" 2>/dev/null && pwd -P)
[ "$root" != "" ] || return 1
apps=$(moos_scoped_teardown_apps_for_root "$root" | tr '\n' ' ')
listing=$(lsof -n -P -w -d cwd -Fnpc 2>/dev/null) || return 1
printf '%s\n' "$listing" | \
awk -v root="$root" -v apps=" $apps " '
/^p/ {
pid = substr($0, 2)
command = ""
is_cwd = 0
next
}
/^c/ {
command = substr($0, 2)
next
}
/^f/ {
is_cwd = ($0 == "fcwd")
next
}
/^n/ && is_cwd {
path = substr($0, 2)
if ((path == root || index(path, root "/") == 1) &&
index(apps, " " command " ") > 0)
print pid
is_cwd = 0
next
}
' | sort -nu
}
moos_scoped_teardown_pids_for_root() {
local root="$1"
moos_scoped_teardown_pids_for_root_procfs "$root" && return 0
moos_scoped_teardown_pids_for_root_lsof "$root" && return 0
return 1
}
moos_scoped_teardown_pids_for_root_checked() {
local root="$1"
if ! moos_scoped_teardown_pids_for_root "$root"; then
echo "moos_scoped_teardown: unable to inspect scoped processes under $root" >&2
return 1
fi
}
moos_scoped_teardown_wait_clear() {
local root="$1"
local grace_seconds="$2"
local pids
local deadline
case "$grace_seconds" in
""|*[!0-9]*)
echo "moos_scoped_teardown: invalid grace period: $grace_seconds" >&2
return 2
;;
esac
deadline=$((SECONDS + grace_seconds))
while :; do
pids=$(moos_scoped_teardown_pids_for_root_checked "$root") || return 2
if [ "$pids" = "" ]; then
return 0
fi
if [ "$SECONDS" -ge "$deadline" ]; then
break
fi
sleep "$MOOS_SCOPED_TEARDOWN_SLEEP"
done
return 1
}
moos_scoped_teardown_signal_pids() {
local signal="$1"
local pids="$2"
local pid
for pid in $pids; do
kill "-$signal" "$pid" >/dev/null 2>&1 || true
done
}
moos_scoped_teardown_stop_root() {
local root="$1"
local pids
local wait_status
if [ "$root" = "" ] || [ ! -d "$root" ]; then
return 0
fi
pids=$(moos_scoped_teardown_pids_for_root_checked "$root") || return 1
if [ "$pids" = "" ]; then
return 0
fi
moos_scoped_teardown_log "INT $root: $pids"
moos_scoped_teardown_signal_pids INT "$pids"
moos_scoped_teardown_wait_clear "$root" "$MOOS_SCOPED_TEARDOWN_GRACE_INT_SECONDS" && return 0
wait_status=$?
if [ "$wait_status" -eq 2 ]; then
return 1
fi
pids=$(moos_scoped_teardown_pids_for_root_checked "$root") || return 1
if [ "$pids" != "" ]; then
moos_scoped_teardown_log "TERM $root: $pids"
moos_scoped_teardown_signal_pids TERM "$pids"
moos_scoped_teardown_wait_clear "$root" "$MOOS_SCOPED_TEARDOWN_GRACE_TERM_SECONDS" && return 0
wait_status=$?
if [ "$wait_status" -eq 2 ]; then
return 1
fi
fi
pids=$(moos_scoped_teardown_pids_for_root_checked "$root") || return 1
if [ "$pids" != "" ]; then
moos_scoped_teardown_log "KILL $root: $pids"
moos_scoped_teardown_signal_pids KILL "$pids"
moos_scoped_teardown_wait_clear "$root" "$MOOS_SCOPED_TEARDOWN_GRACE_KILL_SECONDS" && return 0
wait_status=$?
if [ "$wait_status" -eq 2 ]; then
return 1
fi
fi
pids=$(moos_scoped_teardown_pids_for_root_checked "$root") || return 1
if [ "$pids" != "" ]; then
echo "moos_scoped_teardown: warning: leftover scoped PIDs under $root: $pids" >&2
return 1
fi
return 0
}
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ "$1" = "" ]; then
echo "moos_scoped_teardown.sh ROOT"
echo ""
echo "Stops known MOOS mission apps whose cwd is ROOT or below."
exit 0
fi
moos_scoped_teardown_stop_root "$1"
fi
SHA-256: 308fdd3170939b989d5a6cfea7877fe9608eeec88a57ffbc1c68bf331b10a61c