#!/usr/bin/env bash demo_repository_root() { cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P } demo_is_uint() { [[ ${1:-} =~ ^[0-9]+$ ]] } demo_read_one_line() { local path=$1 destination=$2 value extra IFS= read -r value <"$path" || return 1 if IFS= read -r extra < <(sed -n '2p' "$path") && [[ -n "$extra" ]]; then return 1; fi printf -v "$destination" '%s' "$value" } demo_start_tick() { local pid=$1 stat rest local -a fields demo_is_uint "$pid" || return 1 [[ -r "/proc/$pid/stat" ]] || return 1 stat=$(<"/proc/$pid/stat") rest=${stat##*) } read -r -a fields <<<"$rest" demo_is_uint "${fields[19]:-}" || return 1 printf '%s\n' "${fields[19]}" } demo_process_group() { local pid=$1 pgid pgid=$(ps -o pgid= -p "$pid" 2>/dev/null) || return 1 pgid=${pgid//[[:space:]]/} demo_is_uint "$pgid" || return 1 printf '%s\n' "$pgid" } demo_has_sequence() { local array_name=$1 local -n argv_ref=$array_name shift local -a wanted=("$@") local i j for ((i = 0; i + ${#wanted[@]} <= ${#argv_ref[@]}; i++)); do for ((j = 0; j < ${#wanted[@]}; j++)); do [[ ${argv_ref[i+j]} == "${wanted[j]}" ]] || break done ((j == ${#wanted[@]})) && return 0 done return 1 } demo_command_matches() { local pid=$1 kind=$2 argument base found=0 local -a arguments=() [[ -r "/proc/$pid/cmdline" ]] || return 1 mapfile -d '' -t arguments <"/proc/$pid/cmdline" ((${#arguments[@]} > 0)) || return 1 for argument in "${arguments[@]}"; do base=${argument##*/} if [[ "$kind" == anvil && "$base" == anvil ]]; then found=1; fi if [[ "$kind" == vite && ( "$base" == vite || "$base" == vite.js ) ]]; then found=1; fi done ((found == 1)) || return 1 if [[ "$kind" == anvil ]]; then demo_has_sequence arguments --host 127.0.0.1 || return 1 demo_has_sequence arguments --port 8545 || return 1 demo_has_sequence arguments --chain-id 31337 || return 1 elif [[ "$kind" == vite ]]; then demo_has_sequence arguments web --host 127.0.0.1 --port 5173 || return 1 else return 1 fi } demo_remove_record() { local root=$1 kind=$2 path for path in "$root/.demo/$kind.pid" "$root/.demo/$kind.start" "$root/.demo/$kind.pgid"; do if [[ -e "$path" ]]; then rm -f -- "$path" printf 'Removed %s\n' "${path#"$root/"}" fi done } demo_record_process() { local root=$1 kind=$2 pid=$3 start pgid [[ "$kind" == anvil || "$kind" == vite ]] || return 1 demo_is_uint "$pid" || return 1 start=$(demo_start_tick "$pid") || return 1 pgid=$(demo_process_group "$pid") || return 1 [[ "$pid" == "$pgid" ]] || return 1 demo_command_matches "$pid" "$kind" || return 1 mkdir -p "$root/.demo" chmod 0700 "$root/.demo" printf '%s\n' "$pid" >"$root/.demo/$kind.pid" printf '%s\n' "$start" >"$root/.demo/$kind.start" printf '%s\n' "$pgid" >"$root/.demo/$kind.pgid" chmod 0600 "$root/.demo/$kind.pid" "$root/.demo/$kind.start" "$root/.demo/$kind.pgid" } demo_pid_is_running() { local pid=$1 stat rest state { IFS= read -r stat <"/proc/$pid/stat"; } 2>/dev/null || return 1 rest=${stat##*) } state=${rest%% *} [[ "$state" != Z ]] } demo_stop_recorded() { local root=$1 kind=$2 pid start pgid actual_start actual_pgid local pid_path="$root/.demo/$kind.pid" local start_path="$root/.demo/$kind.start" local pgid_path="$root/.demo/$kind.pgid" [[ "$kind" == anvil || "$kind" == vite ]] || return 1 [[ -e "$pid_path" ]] || return 0 if ! demo_read_one_line "$pid_path" pid || [[ ! "$pid" =~ ^[1-9][0-9]*$ ]] || ((10#$pid <= 1)); then printf 'Refusing invalid %s PID record\n' "$kind" >&2 return 1 fi if ! demo_pid_is_running "$pid"; then printf 'Discarding stale %s process record for PID %s\n' "$kind" "$pid" demo_remove_record "$root" "$kind" return 0 fi if ! demo_read_one_line "$start_path" start || ! demo_is_uint "$start"; then printf 'Refusing incomplete %s start-tick record\n' "$kind" >&2 return 1 fi if ! demo_read_one_line "$pgid_path" pgid || [[ ! "$pgid" =~ ^[1-9][0-9]*$ ]] || ((10#$pgid <= 1)); then printf 'Refusing incomplete %s process-group record\n' "$kind" >&2 return 1 fi actual_start=$(demo_start_tick "$pid") || return 1 actual_pgid=$(demo_process_group "$pid") || return 1 if [[ "$actual_start" != "$start" || "$actual_pgid" != "$pgid" || "$pid" != "$pgid" ]]; then printf 'Refusing %s PID %s: recorded identity does not match\n' "$kind" "$pid" >&2 return 1 fi if ! demo_command_matches "$pid" "$kind"; then printf 'Refusing %s PID %s: command signature does not match\n' "$kind" "$pid" >&2 return 1 fi printf 'Stopping validated %s process group %s\n' "$kind" "$pgid" kill -TERM -- "-$pgid" for _ in {1..50}; do demo_pid_is_running "$pid" || break sleep 0.1 done if demo_pid_is_running "$pid"; then printf 'Escalating validated %s process group %s to KILL\n' "$kind" "$pgid" kill -KILL -- "-$pgid" for _ in {1..20}; do demo_pid_is_running "$pid" || break sleep 0.1 done fi wait "$pid" 2>/dev/null || true demo_pid_is_running "$pid" && { printf 'Validated %s process group %s did not stop\n' "$kind" "$pgid" >&2 return 1 } demo_remove_record "$root" "$kind" }