cloverleaf-larry/lib/nc-status.sh
Bryan Johnson 67cf5fed89 v0.8.29: read/inspect tool validation pass — 7 portability/correctness fixes
Ran every read/analysis tool against the real 24-site integrator (lib + wired
dispatch). Fixed: nc-find --name (GNU sed \+ → POSIX; 0 rows on BSD/macOS),
nc-find tsv/jsonl exit-1-on-success, nc-parse tclproc-refs dropping
digit-leading procs (3M_check_ack), nc-xlate diff missing --site,
nc-diff-interface + nc-smat-diff printf '-'-leading option-injection dropping
output, nc-status not-up crashing on --format, and nc-status not-up's gawk-only
\<up\> word-boundary → portable form (BSD/macOS). Test matrix in Deliverables.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:11:22 -07:00

158 lines
5.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# nc-status.sh — Cloverleaf engine runtime status. Native v3 wrapper around
# the shipped status/tstat binaries — modelled on v1 `status`, `tstats`,
# `tstat`, `thread_status`, `procstatus`, `dstatus`, `connstatus`, `not_up`.
#
# Subcommands:
# sites site-level status (daemon + processes + threads)
# threads thread-level status (port, state, host)
# not-up only threads not in 'up' state
# connections raw connection state (from connstatus binary if available)
# queued threads with messages queued
# raw pass-through to the underlying tstat binary
#
# Flags (where applicable):
# --site SITE single site; default = all sites under $HCIROOT
# --filter REGEX match on thread name
# --format text|tsv default text
#
# This is V1-modeled but native-bash. The exact binary names depend on the
# Cloverleaf version; we auto-discover under $HCIROOT/bin (and server/bin).
set -o pipefail
NC_SELF="$0"
die() { printf 'nc-status: %s\n' "$*" >&2; exit 1; }
warn() { printf 'nc-status: %s\n' "$*" >&2; }
resolve_binary() {
local name="$1"
if command -v "$name" >/dev/null 2>&1; then command -v "$name"; return; fi
for d in "${HCIROOT:-}/bin" "${HCIROOT:-}/server/bin"; do
[ -x "$d/$name" ] && { echo "$d/$name"; return; }
done
return 1
}
list_sites() {
local root="${HCIROOT:-}"
[ -d "$root" ] || die "no \$HCIROOT or it doesn't exist"
find "$root" -mindepth 1 -maxdepth 2 -name NetConfig -type f 2>/dev/null \
| xargs -I{} dirname {} 2>/dev/null \
| xargs -I{} basename {} 2>/dev/null \
| sort -u
}
cmd_sites() {
local target_site=""
local format="text"
while [ $# -gt 0 ]; do
case "$1" in
--site) shift; target_site="$1" ;;
--format) shift; format="$1" ;;
*) die "unknown flag: $1" ;;
esac
shift
done
local hcienginestat; hcienginestat=$(resolve_binary hcienginestat 2>/dev/null || true)
local sites_to_check
if [ -n "$target_site" ]; then sites_to_check="$target_site"; else sites_to_check=$(list_sites); fi
[ "$format" = "tsv" ] && printf 'site\tstate\tdetail\n'
while IFS= read -r s; do
[ -z "$s" ] && continue
local detail="" state="?"
if [ -n "$hcienginestat" ]; then
detail=$(HCISITE="$s" "$hcienginestat" 2>&1 || true)
if echo "$detail" | grep -qiE 'running|active|up'; then state="up"
elif echo "$detail" | grep -qiE 'stopped|down'; then state="down"
else state="unknown"; fi
else
# Fallback: check for lock file / pid file as a poor-man's check
if [ -e "${HCIROOT:-}/$s/lock" ] || [ -e "${HCIROOT:-}/$s/exec/processes" ]; then
state="config-present"
fi
fi
if [ "$format" = "tsv" ]; then
printf '%s\t%s\t%s\n' "$s" "$state" "$(echo "$detail" | head -1)"
else
printf '== %-20s [%s] ==\n%s\n' "$s" "$state" "$(echo "$detail" | head -10)"
fi
done <<< "$sites_to_check"
}
cmd_threads() {
local target_site="${HCISITE:-}"
local filter=""
local format="text"
while [ $# -gt 0 ]; do
case "$1" in
--site) shift; target_site="$1" ;;
--filter) shift; filter="$1" ;;
--format) shift; format="$1" ;;
*) die "unknown flag: $1" ;;
esac
shift
done
local tstat; tstat=$(resolve_binary tstat) || die "tstat binary not found under \$HCIROOT/bin or PATH"
local args=()
[ -n "$target_site" ] && args+=("-s" "$target_site")
local output; output=$(HCISITE="$target_site" "$tstat" "${args[@]}" 2>&1 || true)
if [ -n "$filter" ]; then
output=$(printf '%s' "$output" | grep -E -- "$filter")
fi
if [ "$format" = "tsv" ]; then
# Best-effort: parse the tstat output into TSV
printf '%s\n' "$output" | awk 'NR>1 && NF>0 { gsub(/ +/, "\t"); print }'
else
printf '%s\n' "$output"
fi
}
cmd_not_up() {
local site="${HCISITE:-}"
local filter=""
local format="text"
while [ $# -gt 0 ]; do
case "$1" in
--site) shift; site="$1" ;;
--filter) shift; filter="$1" ;;
# Accept --format and forward it to cmd_threads. The larry.sh dispatch
# ALWAYS appends --format, so without this not-up died "unknown flag:
# --format" and was unusable via the wired tool.
--format) shift; format="$1" ;;
*) die "unknown flag: $1" ;;
esac
shift
done
cmd_threads --site "$site" --format "$format" ${filter:+--filter "$filter"} \
| awk 'NR==1 || tolower($0) !~ /(^|[^a-z])up([^a-z]|$)/'
}
cmd_connections() {
local connstatus; connstatus=$(resolve_binary connstatus 2>/dev/null) || die "connstatus binary not found"
"$connstatus" "$@"
}
cmd_queued() {
local site="${HCISITE:-}"
cmd_threads --site "$site" "$@" \
| awk 'NR==1 || tolower($0) ~ /queued|obq|ibq/'
}
cmd_raw() {
local tstat; tstat=$(resolve_binary tstat) || die "tstat binary not found"
"$tstat" "$@"
}
SUB="${1:-sites}"
case "$SUB" in
sites) shift; cmd_sites "$@" ;;
threads) shift; cmd_threads "$@" ;;
not-up) shift; cmd_not_up "$@" ;;
connections) shift; cmd_connections "$@" ;;
queued) shift; cmd_queued "$@" ;;
raw) shift; cmd_raw "$@" ;;
help|-h|--help) sed -n '2,20p' "$NC_SELF" ;;
*) die "unknown subcommand: $SUB" ;;
esac