diff --git a/tools/pstn-test-check.sh b/tools/pstn-test-check.sh new file mode 100755 index 0000000..7aaac1b --- /dev/null +++ b/tools/pstn-test-check.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# tools/pstn-test-check.sh — Automated half of docs/pstn-sms-test-checklist.md. +# Runs every check that doesn't require an actual phone call or text message +# (container/dir detection, registration, trunk reachability, dialplan +# contexts, kill-switch state, usage-alert timer health, recent call/message +# log activity) and reports PASS/WARN/FAIL for each. What's left after this — +# actually placing a call, texting the DID — needs a second phone and can't +# be scripted; this gets you to that point without re-typing commands or +# re-deriving the container name by hand each time (see the "no such +# container: asterisk" class of failure this script's detection step avoids). +# +# Usage: +# sudo bash tools/pstn-test-check.sh +# +# Safe to run any time — read-only except for the two "asterisk -rx" queries +# below, neither of which changes any state. + +set -uo pipefail + +PASS=0 +WARN=0 +FAIL=0 + +ok() { printf ' [OK] %s\n' "$1"; PASS=$((PASS + 1)); } +warn() { printf ' [WARN] %s\n' "$1"; WARN=$((WARN + 1)); } +fail() { printf ' [FAIL] %s\n' "$1"; FAIL=$((FAIL + 1)); } +section() { printf '\n== %s ==\n' "$1"; } + +if [ "$(id -u)" -ne 0 ]; then + echo "Run with sudo — needs docker exec and (on some boxes) systemctl/journalctl." >&2 + exec sudo bash "$0" "$@" +fi + +# ── Container + directory detection ───────────────────────────────────────── +section "Detecting install" + +CONTAINER="$(docker ps --format '{{.Names}}' 2>/dev/null | grep -m1 -E '^easy-asterisk(-do)?$' || true)" +if [ -z "$CONTAINER" ]; then + fail "No running easy-asterisk / easy-asterisk-do container found — is asterisk installed and started?" + echo "" + echo " $PASS passed, $WARN warnings, $FAIL failed. Stopping — nothing else can be checked without a running container." + exit 1 +fi +ok "Container running: $CONTAINER" + +EA_DIR="" +ACTUAL_USER="${SUDO_USER:-${USER:-root}}" +ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" 2>/dev/null | cut -d: -f6 || echo "/root")" +if [ -d "$ACTUAL_HOME/docker/asterisk-digital-ocean" ]; then + EA_DIR="$ACTUAL_HOME/docker/asterisk-digital-ocean" +elif [ -d "$ACTUAL_HOME/docker/asterisk" ]; then + EA_DIR="$ACTUAL_HOME/docker/asterisk" +fi +if [ -z "$EA_DIR" ]; then + fail "No ~/docker/asterisk or ~/docker/asterisk-digital-ocean found for user $ACTUAL_USER." + exit 1 +fi +ok "Directory: $EA_DIR" + +ASTERISK_DIR="$EA_DIR/config/asterisk" +LOGS_DIR="$EA_DIR/logs" + +# ── Registration ───────────────────────────────────────────────────────────── +section "Extension registration" + +ENDPOINTS_OUT="$(docker exec "$CONTAINER" asterisk -rx "pjsip show endpoints" 2>/dev/null)" +if [ -z "$ENDPOINTS_OUT" ]; then + fail "Could not query pjsip endpoints — is Asterisk actually up inside the container?" +else + # Endpoint lines look like " Endpoint: 101/101 Unavailable 0 of inf" — + # skip the trunk itself (checked separately below) and the header/legend. + while IFS= read -r line; do + ext="$(awk '{print $2}' <<< "$line" | cut -d/ -f1)" + state="$(awk '{print $3}' <<< "$line")" + # Skip the column-header/legend line (" ") + # printed once at the top of real output — it matches the same + # "^ Endpoint:" grep as an actual endpoint row. + [[ "$ext" == "pstn-trunk" || "$ext" == "/dev/null)" + if grep -q "Contact:.*Avail" <<< "$TRUNK_OUT"; then + ok "Trunk contact reachable (Avail)" + else + fail "Trunk contact not Avail — provider unreachable, or the trunk config didn't load. Full output:" + echo "$TRUNK_OUT" | sed 's/^/ /' + fi + + match_count="$(grep -c '^ Match:' <<< "$TRUNK_OUT")" + if [ "$match_count" -gt 0 ]; then + ok "$match_count inbound match IP(s) configured" + else + fail "No inbound match IPs on the trunk identify — inbound calls will never match this trunk" + fi + + for ctx in intercom from-pstn-trunk; do + if docker exec "$CONTAINER" asterisk -rx "dialplan show $ctx" 2>/dev/null | grep -q "not found\|No such context"; then + fail "Dialplan context [$ctx] failed to load" + else + ok "Dialplan context [$ctx] loaded" + fi + done + + # ── Kill-switch ──────────────────────────────────────────────────────────── + section "Spend-cap kill-switch" + KS_FILE="$ASTERISK_DIR/pstn-trunk-killswitch.conf" + if [ -f "$KS_FILE" ]; then + if grep -q '^tripped=1' "$KS_FILE" 2>/dev/null; then + fail "TRIPPED — all PSTN calling is currently blocked. Clear via: sudo ./setup.sh pstn-trunk (update mode)" + else + ok "Not tripped" + fi + else + warn "No pstn-trunk-killswitch.conf found — kill-switch may be disabled (MAX_MONTHLY_SPEND=0)" + fi + + # ── Usage-alert timer ──────────────────────────────────────────────────────── + section "Usage-alert timer (spend/burst checks + kill-switch enforcement)" + if systemctl is-active --quiet pstn-trunk-usage.timer 2>/dev/null; then + ok "systemd timer active" + LAST_RUN="$(systemctl show pstn-trunk-usage.service -p ActiveEnterTimestamp --value 2>/dev/null)" + [ -n "$LAST_RUN" ] && [ "$LAST_RUN" != "n/a" ] && ok "Last ran: $LAST_RUN" || warn "Timer active but hasn't run yet (or timestamp unavailable) — check again in a minute" + elif crontab -l 2>/dev/null | grep -q pstn-trunk-usage; then + ok "cron.d fallback entry present (systemd timer not used on this box)" + else + fail "Neither the systemd timer nor a cron.d entry for pstn-trunk-usage was found — spend alerts and kill-switch enforcement are NOT running" + fi +fi + +# ── SMS inbound ─────────────────────────────────────────────────────────────── +section "SMS inbound" +if systemctl list-unit-files sms-inbound.service &>/dev/null; then + if systemctl is-active --quiet sms-inbound; then + ok "sms-inbound service active" + else + fail "sms-inbound installed but not running — check: journalctl -u sms-inbound -n 50" + fi +else + warn "sms-inbound not installed" +fi + +# ── Recent activity ─────────────────────────────────────────────────────────── +section "Recent activity (last 5 lines per log, if present)" +for log in pstn-trunk-calls.log sip-messages.log; do + f="$LOGS_DIR/$log" + if [ -f "$f" ]; then + echo " -- $log --" + tail -n 5 "$f" 2>/dev/null | sed 's/^/ /' + else + warn "$log not found yet (no activity logged, or the relevant service isn't installed)" + fi +done + +# ── Summary ─────────────────────────────────────────────────────────────────── +section "Summary" +echo " $PASS passed, $WARN warnings, $FAIL failed." +echo "" +echo " This covers everything that can be checked without placing a real call" +echo " or sending a real text. For those, and for what each result above means," +echo " see docs/pstn-sms-test-checklist.md." + +[ "$FAIL" -eq 0 ]