#!/bin/sh
# SecuBox Network Diagnostics RPCD Backend
# Provides DSA switch port statistics and error monitoring

. /usr/share/libubox/jshn.sh

# Error history storage (in-memory via temp files)
HISTORY_DIR="/tmp/secubox-netdiag"
HISTORY_INTERVAL=5
HISTORY_SAMPLES=60  # 5 minutes at 5-second intervals

# Ensure history directory exists
mkdir -p "$HISTORY_DIR" 2>/dev/null

# Helper: Read a sysfs statistic file
read_stat() {
    local iface="$1"
    local stat="$2"
    local path="/sys/class/net/${iface}/statistics/${stat}"
    if [ -f "$path" ]; then
        cat "$path" 2>/dev/null || echo "0"
    else
        echo "0"
    fi
}

# Helper: Get interface link state
get_link_state() {
    local iface="$1"
    local carrier="/sys/class/net/${iface}/carrier"
    local operstate="/sys/class/net/${iface}/operstate"

    if [ -f "$carrier" ] && [ "$(cat "$carrier" 2>/dev/null)" = "1" ]; then
        echo "up"
    elif [ -f "$operstate" ]; then
        cat "$operstate" 2>/dev/null
    else
        echo "unknown"
    fi
}

# Helper: Get DSA master interface
get_dsa_master() {
    local iface="$1"
    local master="/sys/class/net/${iface}/master"
    if [ -L "$master" ]; then
        basename "$(readlink "$master")" 2>/dev/null
    else
        echo ""
    fi
}

# Helper: Get speed and duplex via ethtool
get_ethtool_info() {
    local iface="$1"
    local result

    result=$(ethtool "$iface" 2>/dev/null)
    if [ -n "$result" ]; then
        local speed duplex autoneg link
        speed=$(echo "$result" | grep -i "Speed:" | awk '{print $2}' | sed 's/Mb\/s//')
        duplex=$(echo "$result" | grep -i "Duplex:" | awk '{print $2}')
        autoneg=$(echo "$result" | grep -i "Auto-negotiation:" | awk '{print $2}')
        link=$(echo "$result" | grep -i "Link detected:" | awk '{print $3}')

        json_add_int "speed" "${speed:-0}"
        json_add_string "duplex" "${duplex:-unknown}"
        json_add_string "autoneg" "${autoneg:-unknown}"
        json_add_string "link_detected" "${link:-unknown}"
    else
        json_add_int "speed" 0
        json_add_string "duplex" "unknown"
        json_add_string "autoneg" "unknown"
        json_add_string "link_detected" "unknown"
    fi
}

# Helper: Get all interface statistics
get_interface_stats() {
    local iface="$1"

    json_add_object "stats"
    json_add_string "rx_bytes" "$(read_stat "$iface" rx_bytes)"
    json_add_string "tx_bytes" "$(read_stat "$iface" tx_bytes)"
    json_add_string "rx_packets" "$(read_stat "$iface" rx_packets)"
    json_add_string "tx_packets" "$(read_stat "$iface" tx_packets)"
    json_add_string "rx_dropped" "$(read_stat "$iface" rx_dropped)"
    json_add_string "tx_dropped" "$(read_stat "$iface" tx_dropped)"
    json_close_object
}

# Helper: Get error counters
get_error_stats() {
    local iface="$1"

    json_add_object "errors"
    json_add_string "rx_crc_errors" "$(read_stat "$iface" rx_crc_errors)"
    json_add_string "rx_frame_errors" "$(read_stat "$iface" rx_frame_errors)"
    json_add_string "rx_fifo_errors" "$(read_stat "$iface" rx_fifo_errors)"
    json_add_string "rx_missed_errors" "$(read_stat "$iface" rx_missed_errors)"
    json_add_string "rx_length_errors" "$(read_stat "$iface" rx_length_errors)"
    json_add_string "rx_over_errors" "$(read_stat "$iface" rx_over_errors)"
    json_add_string "tx_aborted_errors" "$(read_stat "$iface" tx_aborted_errors)"
    json_add_string "tx_carrier_errors" "$(read_stat "$iface" tx_carrier_errors)"
    json_add_string "tx_fifo_errors" "$(read_stat "$iface" tx_fifo_errors)"
    json_add_string "tx_heartbeat_errors" "$(read_stat "$iface" tx_heartbeat_errors)"
    json_add_string "tx_window_errors" "$(read_stat "$iface" tx_window_errors)"
    json_add_string "collisions" "$(read_stat "$iface" collisions)"
    json_close_object
}

# Helper: Get ARP/neighbor info for connected devices
get_connected_device() {
    local iface="$1"
    local neighbor

    # Check ARP table for devices on this interface
    neighbor=$(ip neigh show dev "$iface" 2>/dev/null | grep -v "FAILED" | head -1)
    if [ -n "$neighbor" ]; then
        local ip mac
        ip=$(echo "$neighbor" | awk '{print $1}')
        mac=$(echo "$neighbor" | grep -oE '([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}' | head -1)
        json_add_object "neighbor"
        json_add_string "ip" "${ip:-}"
        json_add_string "mac" "${mac:-}"
        json_close_object
    fi
}

# Store error history sample
store_error_sample() {
    local iface="$1"
    local history_file="$HISTORY_DIR/${iface}.history"
    local timestamp=$(date +%s)

    # Collect current error values
    local rx_crc=$(read_stat "$iface" rx_crc_errors)
    local rx_frame=$(read_stat "$iface" rx_frame_errors)
    local rx_fifo=$(read_stat "$iface" rx_fifo_errors)
    local rx_dropped=$(read_stat "$iface" rx_dropped)
    local tx_dropped=$(read_stat "$iface" tx_dropped)
    local collisions=$(read_stat "$iface" collisions)

    # Append to history file
    echo "$timestamp $rx_crc $rx_frame $rx_fifo $rx_dropped $tx_dropped $collisions" >> "$history_file"

    # Keep only last HISTORY_SAMPLES entries
    if [ -f "$history_file" ]; then
        tail -n "$HISTORY_SAMPLES" "$history_file" > "${history_file}.tmp"
        mv "${history_file}.tmp" "$history_file"
    fi
}

# Calculate error rate (errors per minute)
calc_error_rate() {
    local iface="$1"
    local history_file="$HISTORY_DIR/${iface}.history"
    local now=$(date +%s)
    local one_minute_ago=$((now - 60))

    if [ ! -f "$history_file" ]; then
        echo "0"
        return
    fi

    # Get oldest and newest samples within last minute
    local first_sample last_sample
    first_sample=$(awk -v t="$one_minute_ago" '$1 >= t {print; exit}' "$history_file")
    last_sample=$(tail -1 "$history_file")

    if [ -z "$first_sample" ] || [ -z "$last_sample" ]; then
        echo "0"
        return
    fi

    # Calculate delta for rx_crc_errors (column 2)
    local first_crc=$(echo "$first_sample" | awk '{print $2}')
    local last_crc=$(echo "$last_sample" | awk '{print $2}')
    local delta=$((last_crc - first_crc))

    [ "$delta" -lt 0 ] && delta=0
    echo "$delta"
}

# Get error history for sparkline
get_error_history() {
    local iface="$1"
    local minutes="${2:-5}"
    local history_file="$HISTORY_DIR/${iface}.history"
    local now=$(date +%s)
    local start_time=$((now - minutes * 60))

    json_add_array "timeline"

    if [ -f "$history_file" ]; then
        local prev_crc=0
        local first=1

        while read -r line; do
            local ts=$(echo "$line" | awk '{print $1}')
            [ "$ts" -lt "$start_time" ] && continue

            local rx_crc=$(echo "$line" | awk '{print $2}')
            local rx_frame=$(echo "$line" | awk '{print $3}')
            local rx_fifo=$(echo "$line" | awk '{print $4}')

            # Calculate delta from previous sample
            local delta_crc=0
            if [ "$first" = "0" ]; then
                delta_crc=$((rx_crc - prev_crc))
                [ "$delta_crc" -lt 0 ] && delta_crc=0
            fi
            first=0
            prev_crc="$rx_crc"

            json_add_object ""
            json_add_int "timestamp" "$ts"
            json_add_int "rx_crc_errors" "$delta_crc"
            json_add_int "rx_crc_total" "$rx_crc"
            json_add_int "rx_frame_errors" "$rx_frame"
            json_add_int "rx_fifo_errors" "$rx_fifo"
            json_close_object
        done < "$history_file"
    fi

    json_close_array
}

# Method: get_switch_status
# Returns status of all network interfaces with DSA topology
get_switch_status() {
    json_init
    json_add_array "ports"

    # Iterate all network interfaces
    for iface_path in /sys/class/net/*; do
        [ ! -d "$iface_path" ] && continue

        local iface=$(basename "$iface_path")

        # Skip virtual/loopback interfaces
        case "$iface" in
            lo|br-*|docker*|veth*|tun*|tap*) continue ;;
        esac

        # Store error sample for history
        store_error_sample "$iface"

        json_add_object ""
        json_add_string "name" "$iface"

        # Check if this is a DSA port
        local master=$(get_dsa_master "$iface")
        json_add_string "master" "$master"
        json_add_boolean "is_dsa_port" "$([ -n "$master" ] && echo 1 || echo 0)"

        # Link state
        local link_state=$(get_link_state "$iface")
        json_add_string "operstate" "$link_state"
        json_add_boolean "link" "$([ "$link_state" = "up" ] && echo 1 || echo 0)"

        # Speed/duplex from ethtool
        get_ethtool_info "$iface"

        # Traffic statistics
        get_interface_stats "$iface"

        # Error counters
        get_error_stats "$iface"

        # Error rate (errors/minute)
        local error_rate=$(calc_error_rate "$iface")
        json_add_int "error_rate" "$error_rate"

        # Alert level based on error rate
        local alert_level="normal"
        [ "$error_rate" -gt 0 ] && [ "$error_rate" -le 10 ] && alert_level="warning"
        [ "$error_rate" -gt 10 ] && alert_level="critical"
        json_add_string "alert_level" "$alert_level"

        # Connected device info
        get_connected_device "$iface"

        # MAC address
        local mac=""
        [ -f "/sys/class/net/${iface}/address" ] && mac=$(cat "/sys/class/net/${iface}/address" 2>/dev/null)
        json_add_string "mac" "${mac:-}"

        # MTU
        local mtu=""
        [ -f "/sys/class/net/${iface}/mtu" ] && mtu=$(cat "/sys/class/net/${iface}/mtu" 2>/dev/null)
        json_add_int "mtu" "${mtu:-1500}"

        json_close_object
    done

    json_close_array
    json_dump
}

# Method: get_interface_details
# Returns detailed information for a specific interface
get_interface_details() {
    local iface="$1"

    # Validate interface exists
    if [ ! -d "/sys/class/net/${iface}" ]; then
        json_init
        json_add_boolean "error" 1
        json_add_string "message" "Interface not found: $iface"
        json_dump
        return
    fi

    json_init
    json_add_string "interface" "$iface"

    # Full ethtool output
    json_add_object "ethtool"
    local ethtool_out=$(ethtool "$iface" 2>/dev/null)
    if [ -n "$ethtool_out" ]; then
        # Parse key fields
        json_add_string "speed" "$(echo "$ethtool_out" | grep -i 'Speed:' | awk '{print $2}')"
        json_add_string "duplex" "$(echo "$ethtool_out" | grep -i 'Duplex:' | awk '{print $2}')"
        json_add_string "auto_negotiation" "$(echo "$ethtool_out" | grep -i 'Auto-negotiation:' | awk '{print $2}')"
        json_add_string "link_detected" "$(echo "$ethtool_out" | grep -i 'Link detected:' | awk '{print $3}')"
        json_add_string "port" "$(echo "$ethtool_out" | grep -i 'Port:' | cut -d: -f2 | xargs)"
        json_add_string "transceiver" "$(echo "$ethtool_out" | grep -i 'Transceiver:' | awk '{print $2}')"

        # Supported modes
        local modes=$(echo "$ethtool_out" | grep -A20 'Supported link modes:' | grep -E '^\s+[0-9]+' | tr '\n' ' ')
        json_add_string "supported_modes" "$modes"

        # Link partner
        local partner=$(echo "$ethtool_out" | grep -A5 'Link partner' | grep -E '^\s+[0-9]+' | tr '\n' ' ')
        json_add_string "link_partner" "$partner"
    fi
    json_close_object

    # Extended statistics (ethtool -S)
    json_add_object "driver_stats"
    local ethtool_s=$(ethtool -S "$iface" 2>/dev/null)
    if [ -n "$ethtool_s" ]; then
        echo "$ethtool_s" | grep -E '^\s+[a-z_]+:' | while read -r line; do
            local key=$(echo "$line" | cut -d: -f1 | xargs)
            local val=$(echo "$line" | cut -d: -f2 | xargs)
            # Only include first 50 stats to avoid huge output
            [ -n "$key" ] && [ -n "$val" ] && json_add_string "$key" "$val"
        done
    fi
    json_close_object

    # Driver info (ethtool -i)
    json_add_object "driver_info"
    local ethtool_i=$(ethtool -i "$iface" 2>/dev/null)
    if [ -n "$ethtool_i" ]; then
        json_add_string "driver" "$(echo "$ethtool_i" | grep 'driver:' | cut -d: -f2 | xargs)"
        json_add_string "version" "$(echo "$ethtool_i" | grep 'version:' | cut -d: -f2 | xargs)"
        json_add_string "firmware" "$(echo "$ethtool_i" | grep 'firmware-version:' | cut -d: -f2 | xargs)"
        json_add_string "bus_info" "$(echo "$ethtool_i" | grep 'bus-info:' | cut -d: -f2 | xargs)"
    fi
    json_close_object

    # Recent kernel messages
    json_add_array "dmesg"
    dmesg 2>/dev/null | grep -i "$iface" | tail -20 | while read -r line; do
        json_add_string "" "$line"
    done
    json_close_array

    # Current stats
    get_interface_stats "$iface"
    get_error_stats "$iface"

    # Error history
    get_error_history "$iface" 5

    json_dump
}

# Method: get_error_history (standalone)
get_error_history_method() {
    local iface="$1"
    local minutes="${2:-5}"

    if [ ! -d "/sys/class/net/${iface}" ]; then
        json_init
        json_add_boolean "error" 1
        json_add_string "message" "Interface not found: $iface"
        json_dump
        return
    fi

    json_init
    json_add_string "interface" "$iface"
    get_error_history "$iface" "$minutes"
    json_dump
}

# Method: clear_counters
# Clear error history (counters are read-only in sysfs)
clear_counters() {
    local iface="$1"
    local history_file="$HISTORY_DIR/${iface}.history"

    json_init
    if [ -n "$iface" ] && [ -f "$history_file" ]; then
        rm -f "$history_file"
        json_add_boolean "success" 1
        json_add_string "message" "Cleared history for $iface"
    elif [ -z "$iface" ]; then
        rm -f "$HISTORY_DIR"/*.history
        json_add_boolean "success" 1
        json_add_string "message" "Cleared all history"
    else
        json_add_boolean "success" 0
        json_add_string "message" "No history found for $iface"
    fi
    json_dump
}

# Method: get_topology
# Returns DSA switch topology
get_topology() {
    json_init
    json_add_object "topology"

    # Find DSA master interfaces
    json_add_array "switches"

    for master_path in /sys/class/net/*/dsa; do
        [ ! -d "$master_path" ] && continue
        local master=$(dirname "$master_path" | xargs basename)

        json_add_object ""
        json_add_string "master" "$master"
        json_add_string "driver" "$(cat /sys/class/net/${master}/device/driver/module/name 2>/dev/null || echo 'unknown')"

        # Find ports belonging to this master
        json_add_array "ports"
        for port_path in /sys/class/net/*; do
            [ ! -d "$port_path" ] && continue
            local port=$(basename "$port_path")
            local port_master=$(get_dsa_master "$port")

            if [ "$port_master" = "$master" ]; then
                json_add_string "" "$port"
            fi
        done
        json_close_array

        json_close_object
    done

    json_close_array

    # Standalone interfaces (not DSA ports, not virtual)
    json_add_array "standalone"
    for iface_path in /sys/class/net/*; do
        [ ! -d "$iface_path" ] && continue
        local iface=$(basename "$iface_path")
        local master=$(get_dsa_master "$iface")

        # Skip if has DSA master or is virtual
        [ -n "$master" ] && continue
        case "$iface" in
            lo|br-*|docker*|veth*|tun*|tap*) continue ;;
        esac

        # Check if it's a real ethernet device
        [ -f "/sys/class/net/${iface}/device" ] || [ -d "/sys/class/net/${iface}/device" ] && {
            json_add_string "" "$iface"
        }
    done
    json_close_array

    json_close_object
    json_dump
}

# Method: collect_errors
# Collect comprehensive error logs from all interfaces
collect_errors() {
    json_init
    json_add_string "timestamp" "$(date -Iseconds)"
    json_add_string "hostname" "$(cat /proc/sys/kernel/hostname)"

    # System temperature if available
    local temp=""
    for t in /sys/class/thermal/thermal_zone*/temp; do
        [ -f "$t" ] && temp=$(cat "$t" 2>/dev/null)
        break
    done
    [ -n "$temp" ] && json_add_int "temperature" "$((temp / 1000))"

    # Collect errors per interface
    json_add_array "interfaces"
    for iface_path in /sys/class/net/*; do
        [ ! -d "$iface_path" ] && continue
        local iface=$(basename "$iface_path")
        case "$iface" in lo|br-*|docker*|veth*|tun*|tap*) continue ;; esac

        local rx_crc=$(read_stat "$iface" rx_crc_errors)
        local rx_frame=$(read_stat "$iface" rx_frame_errors)
        local rx_fifo=$(read_stat "$iface" rx_fifo_errors)
        local rx_dropped=$(read_stat "$iface" rx_dropped)
        local tx_dropped=$(read_stat "$iface" tx_dropped)
        local collisions=$(read_stat "$iface" collisions)
        local total_err=$((rx_crc + rx_frame + rx_fifo + rx_dropped + tx_dropped + collisions))

        [ "$total_err" -eq 0 ] && continue

        json_add_object ""
        json_add_string "interface" "$iface"
        json_add_int "rx_crc_errors" "$rx_crc"
        json_add_int "rx_frame_errors" "$rx_frame"
        json_add_int "rx_fifo_errors" "$rx_fifo"
        json_add_int "rx_dropped" "$rx_dropped"
        json_add_int "tx_dropped" "$tx_dropped"
        json_add_int "collisions" "$collisions"
        json_add_int "total_errors" "$total_err"
        json_close_object
    done
    json_close_array

    # Recent dmesg errors
    json_add_array "dmesg_errors"
    dmesg 2>/dev/null | grep -iE "error|fail|timeout|reset|link" | tail -30 | while read -r line; do
        json_add_string "" "$line"
    done
    json_close_array

    json_dump
}

# Method: get_port_modes
# Get current and available speed/duplex modes for an interface
get_port_modes() {
    local iface="$1"

    if [ ! -d "/sys/class/net/${iface}" ]; then
        json_init
        json_add_boolean "error" 1
        json_add_string "message" "Interface not found: $iface"
        json_dump
        return
    fi

    json_init
    json_add_string "interface" "$iface"

    # Current settings
    local ethtool_out=$(ethtool "$iface" 2>/dev/null)
    if [ -n "$ethtool_out" ]; then
        local cur_speed=$(echo "$ethtool_out" | grep -i 'Speed:' | head -1 | awk '{print $2}')
        local cur_duplex=$(echo "$ethtool_out" | grep -i 'Duplex:' | head -1 | awk '{print $2}')
        local cur_autoneg=$(echo "$ethtool_out" | grep -E '^\s+Auto-negotiation:' | head -1 | awk '{print $2}')
        local cur_link=$(echo "$ethtool_out" | grep -i 'Link detected:' | head -1 | awk '{print $3}')
        json_add_string "current_speed" "${cur_speed:-unknown}"
        json_add_string "current_duplex" "${cur_duplex:-unknown}"
        json_add_string "autoneg" "${cur_autoneg:-unknown}"
        json_add_string "link" "${cur_link:-unknown}"

        # Parse supported modes
        json_add_array "supported_speeds"
        echo "$ethtool_out" | grep -A50 'Supported link modes:' | grep -oE '[0-9]+base[A-Za-z/]+' | sort -u | while read -r mode; do
            json_add_string "" "$mode"
        done
        json_close_array
    fi

    # EEE status (Energy Efficient Ethernet) - reduces heat
    local eee_out=$(ethtool --show-eee "$iface" 2>/dev/null)
    if [ -n "$eee_out" ]; then
        local eee_enabled=$(echo "$eee_out" | grep -i "EEE status:" | grep -q "enabled" && echo "true" || echo "false")
        local eee_active=$(echo "$eee_out" | grep -i "Link partner" | grep -q "Yes" && echo "true" || echo "false")
        json_add_boolean "eee_enabled" "$eee_enabled"
        json_add_boolean "eee_active" "$eee_active"
        json_add_boolean "eee_supported" "true"
    else
        json_add_boolean "eee_supported" "false"
    fi

    # Wake-on-LAN status
    local wol_out=$(ethtool "$iface" 2>/dev/null | grep -E '^\s+Wake-on:' | head -1 | awk '{print $2}')
    json_add_string "wake_on_lan" "${wol_out:-d}"

    json_dump
}

# Method: set_port_mode
# Set interface speed/duplex/EEE for temperature control
set_port_mode() {
    local iface="$1"
    local speed="$2"
    local duplex="$3"
    local eee="$4"
    local autoneg="$5"

    if [ ! -d "/sys/class/net/${iface}" ]; then
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "Interface not found: $iface"
        json_dump
        return
    fi

    local result=""
    local error=""

    # Set speed/duplex
    if [ -n "$speed" ] && [ -n "$duplex" ]; then
        if [ "$autoneg" = "on" ]; then
            result=$(ethtool -s "$iface" autoneg on 2>&1)
        else
            result=$(ethtool -s "$iface" speed "$speed" duplex "$duplex" autoneg off 2>&1)
        fi
        [ $? -ne 0 ] && error="$result"
    fi

    # Set EEE (Energy Efficient Ethernet) - key for temperature
    if [ -n "$eee" ]; then
        if [ "$eee" = "on" ]; then
            result=$(ethtool --set-eee "$iface" eee on 2>&1)
        else
            result=$(ethtool --set-eee "$iface" eee off 2>&1)
        fi
        [ $? -ne 0 ] && [ -z "$error" ] && error="EEE: $result"
    fi

    json_init
    if [ -z "$error" ]; then
        json_add_boolean "success" 1
        json_add_string "message" "Port mode updated for $iface"

        # Log the change
        logger -t secubox-netdiag "Port mode changed: $iface speed=$speed duplex=$duplex eee=$eee"
    else
        json_add_boolean "success" 0
        json_add_string "error" "$error"
    fi
    json_dump
}

# Method: get_temperature
# Get system and interface temperatures
get_temperature() {
    json_init

    # CPU/SoC temperature
    json_add_array "zones"
    for zone in /sys/class/thermal/thermal_zone*; do
        [ ! -d "$zone" ] && continue
        local name=$(cat "$zone/type" 2>/dev/null || basename "$zone")
        local temp=$(cat "$zone/temp" 2>/dev/null || echo 0)
        json_add_object ""
        json_add_string "name" "$name"
        json_add_int "temp_mc" "$temp"
        json_add_int "temp_c" "$((temp / 1000))"
        json_close_object
    done
    json_close_array

    # hwmon temperatures
    json_add_array "hwmon"
    for hwmon in /sys/class/hwmon/hwmon*; do
        [ ! -d "$hwmon" ] && continue
        local name=$(cat "$hwmon/name" 2>/dev/null || basename "$hwmon")
        for temp_file in "$hwmon"/temp*_input; do
            [ ! -f "$temp_file" ] && continue
            local temp=$(cat "$temp_file" 2>/dev/null || echo 0)
            local label_file="${temp_file%_input}_label"
            local label=$(cat "$label_file" 2>/dev/null || basename "$temp_file" .input)
            json_add_object ""
            json_add_string "sensor" "$name"
            json_add_string "label" "$label"
            json_add_int "temp_mc" "$temp"
            json_add_int "temp_c" "$((temp / 1000))"
            json_close_object
        done
    done
    json_close_array

    json_dump
}

# RPCD list handler
case "$1" in
    list)
        echo '{"get_switch_status":{},"get_interface_details":{"interface":"string"},"get_error_history":{"interface":"string","minutes":5},"clear_counters":{"interface":"string"},"get_topology":{},"collect_errors":{},"get_port_modes":{"interface":"string"},"set_port_mode":{"interface":"string","speed":"string","duplex":"string","eee":"string","autoneg":"string"},"get_temperature":{}}'
        ;;
    call)
        case "$2" in
            get_switch_status)
                get_switch_status
                ;;
            get_interface_details)
                read -r input
                iface=$(echo "$input" | jsonfilter -e '@.interface' 2>/dev/null)
                get_interface_details "$iface"
                ;;
            get_error_history)
                read -r input
                iface=$(echo "$input" | jsonfilter -e '@.interface' 2>/dev/null)
                minutes=$(echo "$input" | jsonfilter -e '@.minutes' 2>/dev/null)
                get_error_history_method "$iface" "${minutes:-5}"
                ;;
            clear_counters)
                read -r input
                iface=$(echo "$input" | jsonfilter -e '@.interface' 2>/dev/null)
                clear_counters "$iface"
                ;;
            get_topology)
                get_topology
                ;;
            collect_errors)
                collect_errors
                ;;
            get_port_modes)
                read -r input
                iface=$(echo "$input" | jsonfilter -e '@.interface' 2>/dev/null)
                get_port_modes "$iface"
                ;;
            set_port_mode)
                read -r input
                iface=$(echo "$input" | jsonfilter -e '@.interface' 2>/dev/null)
                speed=$(echo "$input" | jsonfilter -e '@.speed' 2>/dev/null)
                duplex=$(echo "$input" | jsonfilter -e '@.duplex' 2>/dev/null)
                eee=$(echo "$input" | jsonfilter -e '@.eee' 2>/dev/null)
                autoneg=$(echo "$input" | jsonfilter -e '@.autoneg' 2>/dev/null)
                set_port_mode "$iface" "$speed" "$duplex" "$eee" "$autoneg"
                ;;
            get_temperature)
                get_temperature
                ;;
        esac
        ;;
esac
