#!/bin/bash
################################################################################
# Script Name: network-info-exporter.sh
# Description: Prometheus exporter for Linux network metrics
#
# Collects interface statistics, connection states, routing info, firewall
# rules, DNS configuration, protocol statistics, and latency measurements.
#
# Author:  Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
# Version: 3.0
#
# Usage:
#   # Output to stdout
#   ./network-info-exporter.sh
#
#   # Textfile collector mode (atomic write)
#   ./network-info-exporter.sh --textfile
#
#   # Custom output file
#   ./network-info-exporter.sh -o /path/to/metrics.prom
#
################################################################################

# ============================================================================
# CONFIGURATION VARIABLES
# ============================================================================

TEXTFILE_DIR="/var/lib/node_exporter"
OUTPUT_FILE=""
HOSTNAME=$(hostname)

PING_TARGETS="${PING_TARGETS:-8.8.8.8 1.1.1.1 google.com}"
PING_COUNT="${PING_COUNT:-5}"
PING_TIMEOUT="${PING_TIMEOUT:-2}"

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

show_usage() {
    cat <<EOF
Usage: $0 [OPTIONS]

Export Linux network metrics as Prometheus metrics.

MODES:
    --textfile      Write to node_exporter textfile collector
                    (writes to $TEXTFILE_DIR/network_info.prom)

OPTIONS:
    -o, --output    Output file path (for custom locations)
    -h, --help      Show this help message

EXAMPLES:
    $0                               # Output to stdout
    $0 --textfile                    # Write to textfile collector
    $0 -o /tmp/network.prom          # Write to custom file

EOF
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help) show_usage ;;
            --textfile) OUTPUT_FILE="$TEXTFILE_DIR/network_info.prom"; shift ;;
            -o|--output) OUTPUT_FILE="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; exit 1 ;;
        esac
    done
}

# ============================================================================
# METRIC GENERATION
# ============================================================================

generate_metrics() {
    local START_TIME
    START_TIME=$(date +%s.%N)

# --- System info ---
echo "# HELP network_exporter_info Exporter and system information"
echo "# TYPE network_exporter_info gauge"
local kernel arch os_name os_version
kernel=$(uname -r)
arch=$(uname -m)
if [[ -f /etc/os-release ]]; then
    . /etc/os-release
    os_name="${NAME:-unknown}"
    os_version="${VERSION_ID:-unknown}"
else
    os_name="unknown"
    os_version="unknown"
fi
echo "network_exporter_info{hostname=\"${HOSTNAME}\",kernel=\"${kernel}\",arch=\"${arch}\",os=\"${os_name}\",version=\"${os_version}\"} 1"
echo ""

# --- Interface counters from /proc/net/dev ---
if [[ -r /proc/net/dev ]]; then
    awk '
    NR > 2 {
        sub(/:/, " ")
        iface = $1
        if (iface == "lo") next
        devices[count++] = iface
        rx_bytes[iface]   = $2;  rx_packets[iface] = $3
        rx_errors[iface]  = $4;  rx_dropped[iface] = $5
        tx_bytes[iface]   = $10; tx_packets[iface] = $11
        tx_errors[iface]  = $12; tx_dropped[iface] = $13
    }
    END {
        metrics["receive_bytes"]    = "Total bytes received";    t["receive_bytes"]    = "counter"
        metrics["receive_packets"]  = "Total packets received";  t["receive_packets"]  = "counter"
        metrics["receive_errors"]   = "Total receive errors";    t["receive_errors"]   = "counter"
        metrics["receive_dropped"]  = "Total receive drops";     t["receive_dropped"]  = "counter"
        metrics["transmit_bytes"]   = "Total bytes transmitted"; t["transmit_bytes"]   = "counter"
        metrics["transmit_packets"] = "Total packets transmitted"; t["transmit_packets"] = "counter"
        metrics["transmit_errors"]  = "Total transmit errors";   t["transmit_errors"]  = "counter"
        metrics["transmit_dropped"] = "Total transmit drops";    t["transmit_dropped"] = "counter"

        order[0]="receive_bytes"; order[1]="receive_packets"
        order[2]="receive_errors"; order[3]="receive_dropped"
        order[4]="transmit_bytes"; order[5]="transmit_packets"
        order[6]="transmit_errors"; order[7]="transmit_dropped"

        for (m = 0; m < 8; m++) {
            key = order[m]
            name = "network_" key "_total"
            printf "# HELP %s %s\n", name, metrics[key]
            printf "# TYPE %s %s\n", name, t[key]
            for (i = 0; i < count; i++) {
                d = devices[i]
                if      (key == "receive_bytes")    v = rx_bytes[d]
                else if (key == "receive_packets")  v = rx_packets[d]
                else if (key == "receive_errors")   v = rx_errors[d]
                else if (key == "receive_dropped")  v = rx_dropped[d]
                else if (key == "transmit_bytes")   v = tx_bytes[d]
                else if (key == "transmit_packets") v = tx_packets[d]
                else if (key == "transmit_errors")  v = tx_errors[d]
                else if (key == "transmit_dropped") v = tx_dropped[d]
                printf "%s{device=\"%s\",hostname=\"%s\"} %s\n", name, d, ENVIRON["HOSTNAME"], v
            }
            print ""
        }
    }
    ' /proc/net/dev
fi

# --- Interface status, MTU, queue length, speed ---
echo "# HELP network_interface_up Interface operational status (1=up, 0=down)"
echo "# TYPE network_interface_up gauge"
echo "# HELP network_interface_mtu Interface MTU size"
echo "# TYPE network_interface_mtu gauge"
echo "# HELP network_interface_tx_queue_length Interface transmit queue length"
echo "# TYPE network_interface_tx_queue_length gauge"
echo "# HELP network_interface_speed_mbps Interface link speed in Mbps"
echo "# TYPE network_interface_speed_mbps gauge"

for iface_path in /sys/class/net/*; do
    iface=$(basename "$iface_path")
    [[ "$iface" == "lo" ]] && continue

    # status
    local operstate
    operstate=$(cat "$iface_path/operstate" 2>/dev/null) || operstate="unknown"
    if [[ "$operstate" == "up" ]]; then
        echo "network_interface_up{device=\"${iface}\",hostname=\"${HOSTNAME}\"} 1"
    else
        echo "network_interface_up{device=\"${iface}\",hostname=\"${HOSTNAME}\"} 0"
    fi

    # mtu
    local mtu
    mtu=$(cat "$iface_path/mtu" 2>/dev/null) || mtu=0
    echo "network_interface_mtu{device=\"${iface}\",hostname=\"${HOSTNAME}\"} ${mtu}"

    # tx queue length
    local qlen
    qlen=$(cat "$iface_path/tx_queue_len" 2>/dev/null) || qlen=0
    echo "network_interface_tx_queue_length{device=\"${iface}\",hostname=\"${HOSTNAME}\"} ${qlen}"

    # speed (only for physical interfaces with valid speed)
    if [[ -r "$iface_path/speed" ]]; then
        local speed
        speed=$(cat "$iface_path/speed" 2>/dev/null) || speed=-1
        if [[ "$speed" =~ ^[0-9]+$ ]] && [[ "$speed" -gt 0 ]]; then
            echo "network_interface_speed_mbps{device=\"${iface}\",hostname=\"${HOSTNAME}\"} ${speed}"
        fi
    fi
done
echo ""

# --- Routing ---
echo "# HELP network_routes_total Route count by protocol"
echo "# TYPE network_routes_total gauge"
if command -v ip &>/dev/null; then
    local ipv4_routes ipv6_routes default_routes
    ipv4_routes=$(ip route show 2>/dev/null | wc -l)
    ipv6_routes=$(ip -6 route show 2>/dev/null | wc -l)
    default_routes=$(ip route show default 2>/dev/null | wc -l)
    echo "network_routes_total{family=\"ipv4\",hostname=\"${HOSTNAME}\"} ${ipv4_routes}"
    echo "network_routes_total{family=\"ipv6\",hostname=\"${HOSTNAME}\"} ${ipv6_routes}"
    echo "network_routes_total{type=\"default\",hostname=\"${HOSTNAME}\"} ${default_routes}"
fi
echo ""

# --- DNS nameservers ---
echo "# HELP network_dns_nameservers_configured Number of configured DNS nameservers"
echo "# TYPE network_dns_nameservers_configured gauge"
local ns_count=0
if [[ -r /etc/resolv.conf ]]; then
    ns_count=$(grep -c "^nameserver" /etc/resolv.conf 2>/dev/null) || ns_count=0
fi
echo "network_dns_nameservers_configured{hostname=\"${HOSTNAME}\"} ${ns_count}"
echo ""

# --- Firewall rules ---
echo "# HELP network_firewall_rules_total Firewall rule count"
echo "# TYPE network_firewall_rules_total gauge"
local ipt_rules=0 ip6t_rules=0
if command -v iptables &>/dev/null; then
    ipt_rules=$(iptables -S 2>/dev/null | grep -c '^-A' 2>/dev/null) || ipt_rules=0
fi
if command -v ip6tables &>/dev/null; then
    ip6t_rules=$(ip6tables -S 2>/dev/null | grep -c '^-A' 2>/dev/null) || ip6t_rules=0
fi
echo "network_firewall_rules_total{family=\"ipv4\",hostname=\"${HOSTNAME}\"} ${ipt_rules}"
echo "network_firewall_rules_total{family=\"ipv6\",hostname=\"${HOSTNAME}\"} ${ip6t_rules}"
echo ""

# --- Network service status ---
if command -v systemctl &>/dev/null; then
    echo "# HELP network_service_active Network service status (1=active, 0=inactive)"
    echo "# TYPE network_service_active gauge"
    for svc in NetworkManager networking systemd-networkd dhcpcd wpa_supplicant; do
        local status=0
        systemctl is-active "$svc" &>/dev/null && status=1
        echo "network_service_active{service=\"${svc}\",hostname=\"${HOSTNAME}\"} ${status}"
    done
    echo ""
fi

# --- Socket stats from /proc/net/sockstat ---
if [[ -r /proc/net/sockstat ]]; then
    echo "# HELP network_sockets_inuse Sockets currently in use by protocol"
    echo "# TYPE network_sockets_inuse gauge"
    local tcp_inuse udp_inuse raw_inuse
    tcp_inuse=$(awk '/^TCP:/ {print $3}' /proc/net/sockstat 2>/dev/null) || tcp_inuse=0
    udp_inuse=$(awk '/^UDP:/ {print $3}' /proc/net/sockstat 2>/dev/null) || udp_inuse=0
    raw_inuse=$(awk '/^RAW:/ {print $3}' /proc/net/sockstat 2>/dev/null) || raw_inuse=0
    echo "network_sockets_inuse{protocol=\"tcp\",hostname=\"${HOSTNAME}\"} ${tcp_inuse:-0}"
    echo "network_sockets_inuse{protocol=\"udp\",hostname=\"${HOSTNAME}\"} ${udp_inuse:-0}"
    echo "network_sockets_inuse{protocol=\"raw\",hostname=\"${HOSTNAME}\"} ${raw_inuse:-0}"
    echo ""
fi

# --- TCP retransmissions and listen overflows from /proc/net/netstat ---
if [[ -r /proc/net/netstat ]]; then
    local tcp_retrans=0 listen_overflows=0 listen_drops=0
    # Parse TcpExt header+values pair
    local headers values
    headers=$(grep "^TcpExt:" /proc/net/netstat | head -1)
    values=$(grep "^TcpExt:" /proc/net/netstat | tail -1)
    if [[ -n "$headers" && -n "$values" ]]; then
        tcp_retrans=$(paste <(echo "$headers" | tr ' ' '\n') <(echo "$values" | tr ' ' '\n') | awk -F'\t' '$1=="TCPRetransSegs" {print $2}')
        listen_overflows=$(paste <(echo "$headers" | tr ' ' '\n') <(echo "$values" | tr ' ' '\n') | awk -F'\t' '$1=="ListenOverflows" {print $2}')
        listen_drops=$(paste <(echo "$headers" | tr ' ' '\n') <(echo "$values" | tr ' ' '\n') | awk -F'\t' '$1=="ListenDrops" {print $2}')
    fi

    echo "# HELP network_tcp_retransmit_segments_total TCP segments retransmitted"
    echo "# TYPE network_tcp_retransmit_segments_total counter"
    echo "network_tcp_retransmit_segments_total{hostname=\"${HOSTNAME}\"} ${tcp_retrans:-0}"

    echo ""
    echo "# HELP network_tcp_listen_overflows_total TCP listen queue overflows"
    echo "# TYPE network_tcp_listen_overflows_total counter"
    echo "network_tcp_listen_overflows_total{hostname=\"${HOSTNAME}\"} ${listen_overflows:-0}"

    echo ""
    echo "# HELP network_tcp_listen_drops_total TCP listen queue drops"
    echo "# TYPE network_tcp_listen_drops_total counter"
    echo "network_tcp_listen_drops_total{hostname=\"${HOSTNAME}\"} ${listen_drops:-0}"
    echo ""
fi

# --- ARP table ---
if [[ -r /proc/net/arp ]]; then
    echo "# HELP network_arp_entries_total ARP table entries"
    echo "# TYPE network_arp_entries_total gauge"
    local arp_count
    arp_count=$(($(wc -l < /proc/net/arp) - 1))
    [[ $arp_count -lt 0 ]] && arp_count=0
    echo "network_arp_entries_total{hostname=\"${HOSTNAME}\"} ${arp_count}"
    echo ""
fi

# --- UDP sockets ---
if [[ -r /proc/net/udp ]]; then
    echo "# HELP network_udp_sockets_total UDP sockets"
    echo "# TYPE network_udp_sockets_total gauge"
    local udp_sockets
    udp_sockets=$(($(wc -l < /proc/net/udp) - 1))
    [[ $udp_sockets -lt 0 ]] && udp_sockets=0
    echo "network_udp_sockets_total{hostname=\"${HOSTNAME}\"} ${udp_sockets}"
    echo ""
fi

# --- ICMP stats from /proc/net/snmp ---
if [[ -r /proc/net/snmp ]]; then
    local icmp_header icmp_values
    icmp_header=$(grep "^Icmp:" /proc/net/snmp | head -1)
    icmp_values=$(grep "^Icmp:" /proc/net/snmp | tail -1)
    if [[ -n "$icmp_header" && -n "$icmp_values" ]]; then
        local icmp_in icmp_out icmp_err_in icmp_err_out
        icmp_in=$(paste <(echo "$icmp_header" | tr ' ' '\n') <(echo "$icmp_values" | tr ' ' '\n') | awk -F'\t' '$1=="InMsgs" {print $2}')
        icmp_out=$(paste <(echo "$icmp_header" | tr ' ' '\n') <(echo "$icmp_values" | tr ' ' '\n') | awk -F'\t' '$1=="OutMsgs" {print $2}')
        icmp_err_in=$(paste <(echo "$icmp_header" | tr ' ' '\n') <(echo "$icmp_values" | tr ' ' '\n') | awk -F'\t' '$1=="InErrors" {print $2}')
        icmp_err_out=$(paste <(echo "$icmp_header" | tr ' ' '\n') <(echo "$icmp_values" | tr ' ' '\n') | awk -F'\t' '$1=="OutErrors" {print $2}')

        echo "# HELP network_icmp_messages_total ICMP messages by direction"
        echo "# TYPE network_icmp_messages_total counter"
        echo "network_icmp_messages_total{direction=\"in\",hostname=\"${HOSTNAME}\"} ${icmp_in:-0}"
        echo "network_icmp_messages_total{direction=\"out\",hostname=\"${HOSTNAME}\"} ${icmp_out:-0}"

        echo ""
        echo "# HELP network_icmp_errors_total ICMP errors by direction"
        echo "# TYPE network_icmp_errors_total counter"
        echo "network_icmp_errors_total{direction=\"in\",hostname=\"${HOSTNAME}\"} ${icmp_err_in:-0}"
        echo "network_icmp_errors_total{direction=\"out\",hostname=\"${HOSTNAME}\"} ${icmp_err_out:-0}"
        echo ""
    fi
fi

# --- TCP connections by state ---
if [[ -r /proc/net/tcp ]]; then
    echo "# HELP network_tcp_connections TCP connections by state"
    echo "# TYPE network_tcp_connections gauge"
    # hex state codes: 01=ESTABLISHED, 06=TIME_WAIT, 0A=LISTEN
    local tcp_estab tcp_listen tcp_tw tcp_close_wait tcp_syn_recv
    tcp_estab=$(awk '$4=="01" {c++} END {print c+0}' /proc/net/tcp 2>/dev/null)
    tcp_listen=$(awk '$4=="0A" {c++} END {print c+0}' /proc/net/tcp 2>/dev/null)
    tcp_tw=$(awk '$4=="06" {c++} END {print c+0}' /proc/net/tcp 2>/dev/null)
    tcp_close_wait=$(awk '$4=="08" {c++} END {print c+0}' /proc/net/tcp 2>/dev/null)
    tcp_syn_recv=$(awk '$4=="03" {c++} END {print c+0}' /proc/net/tcp 2>/dev/null)
    echo "network_tcp_connections{state=\"established\",hostname=\"${HOSTNAME}\"} ${tcp_estab}"
    echo "network_tcp_connections{state=\"listen\",hostname=\"${HOSTNAME}\"} ${tcp_listen}"
    echo "network_tcp_connections{state=\"time_wait\",hostname=\"${HOSTNAME}\"} ${tcp_tw}"
    echo "network_tcp_connections{state=\"close_wait\",hostname=\"${HOSTNAME}\"} ${tcp_close_wait}"
    echo "network_tcp_connections{state=\"syn_recv\",hostname=\"${HOSTNAME}\"} ${tcp_syn_recv}"
    echo ""
fi

# --- Conntrack ---
if [[ -r /proc/sys/net/netfilter/nf_conntrack_count ]]; then
    echo "# HELP network_nf_conntrack_entries Conntrack table entries"
    echo "# TYPE network_nf_conntrack_entries gauge"
    echo "network_nf_conntrack_entries{hostname=\"${HOSTNAME}\"} $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null || echo 0)"
fi
if [[ -r /proc/sys/net/netfilter/nf_conntrack_max ]]; then
    echo "# HELP network_nf_conntrack_entries_limit Conntrack table maximum"
    echo "# TYPE network_nf_conntrack_entries_limit gauge"
    echo "network_nf_conntrack_entries_limit{hostname=\"${HOSTNAME}\"} $(cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null || echo 0)"
    echo ""
fi

# --- Latency (parallel ping) ---
if command -v ping &>/dev/null; then
    read -ra targets <<< "$PING_TARGETS"
    local tmp_files=() pids=() labels=()

    for target in "${targets[@]}"; do
        [[ -z "$target" ]] && continue
        local label
        label=$(echo "$target" | tr -c 'a-zA-Z0-9._-' '_' | sed 's/_*$//')
        labels+=("$label")

        local tmp
        tmp=$(mktemp)
        tmp_files+=("$tmp")

        (
            set +e
            timeout $((PING_COUNT * PING_TIMEOUT + 5)) ping -c "$PING_COUNT" -W "$PING_TIMEOUT" "$target" > "$tmp" 2>&1
            echo "EXIT=$?" >> "$tmp"
        ) &
        pids+=($!)
    done

    [[ ${#pids[@]} -gt 0 ]] && wait "${pids[@]}"

    # Collect parsed results
    local all_min=() all_avg=() all_max=() all_stddev=() all_loss=()

    for i in "${!tmp_files[@]}"; do
        local tmp="${tmp_files[$i]}"
        local exit_code min_v=0 avg_v=0 max_v=0 stddev_v=0 loss_v=100

        if [[ -f "$tmp" ]]; then
            exit_code=$(grep "^EXIT=" "$tmp" | tail -1 | cut -d= -f2)
            if [[ "${exit_code:-1}" -eq 0 ]]; then
                local stats
                stats=$(grep -E 'rtt min/avg/max/(mdev|stddev)' "$tmp" | head -1)
                if [[ -n "$stats" ]]; then
                    local vals
                    vals=$(echo "$stats" | cut -d= -f2 | awk '{print $1}')
                    min_v=$(echo "$vals" | cut -d/ -f1)
                    avg_v=$(echo "$vals" | cut -d/ -f2)
                    max_v=$(echo "$vals" | cut -d/ -f3)
                    stddev_v=$(echo "$vals" | cut -d/ -f4)
                fi
                loss_v=$(grep -oP '\d+(?=% packet loss)' "$tmp" | head -1)
            fi
            rm -f "$tmp"
        fi

        all_min+=("${min_v:-0}")
        all_avg+=("${avg_v:-0}")
        all_max+=("${max_v:-0}")
        all_stddev+=("${stddev_v:-0}")
        all_loss+=("${loss_v:-100}")
    done

    echo "# HELP network_ping_rtt_min_milliseconds Minimum ping RTT"
    echo "# TYPE network_ping_rtt_min_milliseconds gauge"
    for i in "${!labels[@]}"; do
        echo "network_ping_rtt_min_milliseconds{target=\"${labels[$i]}\",hostname=\"${HOSTNAME}\"} ${all_min[$i]}"
    done

    echo ""
    echo "# HELP network_ping_rtt_avg_milliseconds Average ping RTT"
    echo "# TYPE network_ping_rtt_avg_milliseconds gauge"
    for i in "${!labels[@]}"; do
        echo "network_ping_rtt_avg_milliseconds{target=\"${labels[$i]}\",hostname=\"${HOSTNAME}\"} ${all_avg[$i]}"
    done

    echo ""
    echo "# HELP network_ping_rtt_max_milliseconds Maximum ping RTT"
    echo "# TYPE network_ping_rtt_max_milliseconds gauge"
    for i in "${!labels[@]}"; do
        echo "network_ping_rtt_max_milliseconds{target=\"${labels[$i]}\",hostname=\"${HOSTNAME}\"} ${all_max[$i]}"
    done

    echo ""
    echo "# HELP network_ping_rtt_stddev_milliseconds Ping RTT standard deviation (jitter)"
    echo "# TYPE network_ping_rtt_stddev_milliseconds gauge"
    for i in "${!labels[@]}"; do
        echo "network_ping_rtt_stddev_milliseconds{target=\"${labels[$i]}\",hostname=\"${HOSTNAME}\"} ${all_stddev[$i]}"
    done

    echo ""
    echo "# HELP network_ping_packet_loss_percent Packet loss percentage"
    echo "# TYPE network_ping_packet_loss_percent gauge"
    for i in "${!labels[@]}"; do
        echo "network_ping_packet_loss_percent{target=\"${labels[$i]}\",hostname=\"${HOSTNAME}\"} ${all_loss[$i]}"
    done
    echo ""
fi

# --- Exporter timing ---
local END_TIME DURATION
END_TIME=$(date +%s.%N)
DURATION=$(echo "$END_TIME - $START_TIME" | bc)

echo "# HELP network_exporter_duration_seconds Time to generate all metrics"
echo "# TYPE network_exporter_duration_seconds gauge"
echo "network_exporter_duration_seconds{hostname=\"${HOSTNAME}\"} ${DURATION}"

echo ""
echo "# HELP network_exporter_last_run_timestamp Unix timestamp of last successful run"
echo "# TYPE network_exporter_last_run_timestamp gauge"
echo "network_exporter_last_run_timestamp{hostname=\"${HOSTNAME}\"} $(date +%s)"
}

# ============================================================================
# MAIN EXECUTION
# ============================================================================

main() {
    parse_args "$@"

    if [[ $EUID -ne 0 ]]; then
        echo "Error: This script must be run as root" >&2
        exit 1
    fi

    if [ -n "$OUTPUT_FILE" ]; then
        local output_dir
        output_dir="$(dirname "$OUTPUT_FILE")"
        mkdir -p "$output_dir"

        local temp_file
        temp_file=$(mktemp "${output_dir}/.network_info.XXXXXX")

        if ! generate_metrics > "$temp_file" 2>/dev/null; then
            rm -f "$temp_file"
            echo "ERROR: Failed to generate metrics" >&2
            exit 1
        fi

        local file_lines
        file_lines=$(wc -l < "$temp_file" 2>/dev/null || echo 0)

        if [ "$file_lines" -lt 10 ]; then
            rm -f "$temp_file"
            echo "ERROR: Metrics file too small ($file_lines lines), keeping previous" >&2
            exit 1
        fi

        chmod 644 "$temp_file"
        mv -f "$temp_file" "$OUTPUT_FILE"

        echo "Metrics written to $OUTPUT_FILE ($file_lines lines)" >&2
    else
        generate_metrics
    fi
}

main "$@"
