Trunk Monitor with State-Change Alerts

Monitoring -- Last reviewed 2026-03-29 monitoring trunk slack cron pjsip Found this useful? Upvote it. ×

Trunk Monitor with State-Change Alerts

Unlike simple "check and alert every time" monitors, this script tracks trunk state in a file and only sends a notification when the state changes. You get one "trunk is DOWN" alert and one "trunk has RECOVERED" alert, not 288 identical alerts per day from a 5-minute cron job.

The Script

#!/bin/bash
# trunk-monitor.sh - SIP trunk registration monitor with state tracking
# Install: /opt/pbx-scripts/trunk-monitor.sh
# Cron:    */5 * * * * root /opt/pbx-scripts/trunk-monitor.sh

STATE_FILE="/var/run/asterisk/trunk-monitor.state"
SLACK_WEBHOOK="$(asterisk -rx 'dialplan show globals' 2>/dev/null | grep SLACK_WEBHOOK | cut -d'=' -f2-)"
TRUNK_NAME="my-trunk-reg"
HOSTNAME=$(hostname -f)

mkdir -p "$(dirname "$STATE_FILE")"

# Send Slack notification with color-coded attachment
send_slack() {
    local message="$1"
    local color="$2"  # "danger" for down, "good" for up

    if [ -z "$SLACK_WEBHOOK" ] || [ "$SLACK_WEBHOOK" = " " ]; then
        logger -t trunk-monitor "Slack webhook not configured"
        return
    fi

    local payload
    payload=$(jq -n \
        --arg color "$color" \
        --arg title "PBX Trunk Alert - $HOSTNAME" \
        --arg text "$message" \
        --argjson ts "$(date +%s)" \
        '{attachments: [{color: $color, title: $title, text: $text, ts: $ts}]}')

    curl -s -X POST -H 'Content-type: application/json' \
        --data "$payload" "$SLACK_WEBHOOK" > /dev/null 2>&1
}

# Check trunk registration status
check_trunk() {
    local output
    output=$(asterisk -rx 'pjsip show registrations' 2>/dev/null)

    if echo "$output" | grep -q "$TRUNK_NAME.*Registered"; then
        echo "UP"
    elif echo "$output" | grep -q "$TRUNK_NAME"; then
        echo "DOWN"
    else
        echo "MISSING"
    fi
}

# State management
get_previous_state() {
    [ -f "$STATE_FILE" ] && cat "$STATE_FILE" || echo "UNKNOWN"
}

CURRENT_STATE=$(check_trunk)
PREVIOUS_STATE=$(get_previous_state)

logger -t trunk-monitor "Check: ${TRUNK_NAME} is ${CURRENT_STATE} (was ${PREVIOUS_STATE})"

# Only alert on state change
if [ "$CURRENT_STATE" != "$PREVIOUS_STATE" ]; then
    case "$CURRENT_STATE" in
        UP)
            if [ "$PREVIOUS_STATE" = "DOWN" ] || [ "$PREVIOUS_STATE" = "MISSING" ]; then
                send_slack "Trunk *${TRUNK_NAME}* has RECOVERED." "good"
                logger -t trunk-monitor "RECOVERED: ${TRUNK_NAME}"
            fi
            ;;
        DOWN)
            send_slack "Trunk *${TRUNK_NAME}* is DOWN - registration failed." "danger"
            logger -t trunk-monitor "DOWN: ${TRUNK_NAME}"
            ;;
        MISSING)
            send_slack "Trunk *${TRUNK_NAME}* is MISSING from config." "danger"
            logger -t trunk-monitor "MISSING: ${TRUNK_NAME}"
            ;;
    esac
    echo "$CURRENT_STATE" > "$STATE_FILE"
fi

case "$CURRENT_STATE" in
    UP) exit 0 ;;
    *) exit 1 ;;
esac

How It Works

State tracking: The script writes the current state (UP, DOWN, or MISSING) to a file in /var/run/asterisk/. On each run, it compares current state to the saved state. Only if they differ does it fire a notification.

State transitions that trigger alerts: - UP -> DOWN or UP -> MISSING: sends a red "danger" alert - DOWN -> UP or MISSING -> UP: sends a green "good" recovery alert - UP -> UP, DOWN -> DOWN: no notification (no change)

Webhook sourcing: The script reads the Slack webhook URL from Asterisk's global variables (dialplan show globals). This keeps the webhook in one place (your extensions.conf) rather than duplicating it in scripts.

Installation

sudo cp trunk-monitor.sh /opt/pbx-scripts/
sudo chmod +x /opt/pbx-scripts/trunk-monitor.sh

# Add cron job
echo '*/5 * * * * root /opt/pbx-scripts/trunk-monitor.sh >/dev/null 2>&1' | \
    sudo tee /etc/cron.d/trunk-monitor

Checking State Manually

# See current tracked state
cat /var/run/asterisk/trunk-monitor.state

# See recent checks in syslog
journalctl -t trunk-monitor --since "1 hour ago"

Extending for Multiple Trunks

To monitor multiple trunks, loop over an array:

TRUNKS=("primary-trunk" "backup-trunk")

for TRUNK_NAME in "${TRUNKS[@]}"; do
    STATE_FILE="/var/run/asterisk/trunk-${TRUNK_NAME}.state"
    CURRENT_STATE=$(check_trunk)
    PREVIOUS_STATE=$(get_previous_state)
    # ... same alert logic ...
done

User Notes

No notes yet. Be the first to contribute a tip or example.

Contribute a note

Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.

Moderated before publishing. Email never shown.

Related Snippets