fix(installer): survive self-modification mid-run; sturdier unit detection
Root cause of the mixed-up second install on the A2000 host: when run from the clone it manages, the script's own git checkout/merge REPLACES scripts/install.sh while bash is still executing it. Bash reads scripts lazily by byte offset, so it resumed parsing the NEW file at the OLD offset and executed an arbitrary tail of it — observed as the fresh- service path running on a host whose unit already existed: the port scan saw the still-running old service on 3000 and silently wrote a new unit on 3001, while enable --now on the already-active service changed nothing until a manual daemon-reload. Fix: the whole script now runs inside main(), invoked as 'main "$@"; exit $?' so bash parses everything up front and never reads the file again after main returns (the exit lives in the same parse unit — demonstrated necessary: with a bare 'main "$@"' ending, bash still executed the swapped file's trailing content after main returned). Also: unit existence is now checked with 'systemctl cat' instead of grepping the full list-unit-files output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+17
-1
@@ -32,6 +32,16 @@ say() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
|||||||
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
|
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
|
||||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# The entire remainder runs inside main(), invoked on the LAST line. This
|
||||||
|
# makes the script safe against being MODIFIED WHILE RUNNING: when executed
|
||||||
|
# from the clone it manages, the git checkout below replaces this very file,
|
||||||
|
# and bash reads scripts lazily by byte offset — without this wrapper it
|
||||||
|
# resumes parsing the NEW file at the OLD offset and executes an arbitrary
|
||||||
|
# tail of it (observed: the fresh-service path ran on a host whose unit
|
||||||
|
# already existed). With main(), the whole script is parsed before any of
|
||||||
|
# it executes.
|
||||||
|
main() {
|
||||||
|
|
||||||
OS="$(uname -s)"
|
OS="$(uname -s)"
|
||||||
ARCH="$(uname -m)"
|
ARCH="$(uname -m)"
|
||||||
|
|
||||||
@@ -131,7 +141,7 @@ if [ "$SYS_SUDO" = "__none__" ] && [ "$NO_SERVICE" -eq 0 ]; then
|
|||||||
NO_SERVICE=1
|
NO_SERVICE=1
|
||||||
fi
|
fi
|
||||||
if [ "$OS" = "Linux" ] && [ "$NO_SERVICE" -eq 0 ] && command -v systemctl >/dev/null; then
|
if [ "$OS" = "Linux" ] && [ "$NO_SERVICE" -eq 0 ] && command -v systemctl >/dev/null; then
|
||||||
if systemctl list-unit-files 2>/dev/null | grep -q '^socktop-agent\.service'; then
|
if systemctl cat socktop-agent.service >/dev/null 2>&1; then
|
||||||
# UPGRADE: the unit file is the operator's (SSL, tokens, ports may be
|
# UPGRADE: the unit file is the operator's (SSL, tokens, ports may be
|
||||||
# configured there) — never overwrite it. Only the binary it points at
|
# configured there) — never overwrite it. Only the binary it points at
|
||||||
# is replaced, then the service is restarted.
|
# is replaced, then the service is restarted.
|
||||||
@@ -228,3 +238,9 @@ say "Installed:"
|
|||||||
"$PREFIX/socktop_agent" --version
|
"$PREFIX/socktop_agent" --version
|
||||||
say "Active on PATH: $(type -p socktop || true) / $(type -p socktop_agent || true)"
|
say "Active on PATH: $(type -p socktop || true) / $(type -p socktop_agent || true)"
|
||||||
socktop --version
|
socktop --version
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# exit in the same parse unit as the call: after main returns, bash must not
|
||||||
|
# read another byte from this (possibly replaced) file.
|
||||||
|
main "$@"; exit $?
|
||||||
|
|||||||
Reference in New Issue
Block a user