- commands/block/: block.sh, show.sh, helpers.sh - commands/unblock/: unblock.sh, show.sh, helpers.sh - flag::define array type: --ip[], --subnet[], --port[], --service[] - help.sh: use pre-cached _FLAG_C_* arrays instead of flag::_parse_constraints - remove flag::_parse_constraints/flag::_constraint_get calls from help.sh - adopt local var; var=value pattern for safe assignment
323 lines
No EOL
11 KiB
Bash
323 lines
No EOL
11 KiB
Bash
#!/usr/bin/env bash
|
|
# commands/activity/helpers.sh
|
|
# Implementation logic — render functions, data fetching, maps
|
|
|
|
# cmd::activity::_impl <filter_peer> <filter_service> <filter_ip> <filter_type>
|
|
# <hours> <accept_only> <drop_only> <external_only>
|
|
# <show_ports> <exclude_str>
|
|
function cmd::activity::_impl() {
|
|
local filter_peer="$1" filter_service="$2" filter_ip="$3" filter_type="$4"
|
|
local hours="$5"
|
|
local accept_only="$6" drop_only="$7" external_only="$8"
|
|
local show_ports="$9" exclude_str="${10}"
|
|
|
|
if [[ -n "$filter_peer" && -n "$filter_type" ]]; then
|
|
filter_peer=$(peers::resolve_and_require "$filter_peer" "$filter_type") || return 1
|
|
fi
|
|
|
|
local service_ip=""
|
|
if [[ -n "$filter_service" ]]; then
|
|
service_ip=$(net::resolve "$filter_service" 2>/dev/null | head -1 | cut -d: -f1) || true
|
|
[[ -z "$service_ip" ]] && log::error "Service not found: ${filter_service}" && return 1
|
|
fi
|
|
[[ -n "$filter_ip" ]] && service_ip="$filter_ip"
|
|
|
|
# ── Fetch data ──
|
|
local data=""
|
|
if [[ "$accept_only" != "true" ]]; then
|
|
data=$(json::activity_aggregate \
|
|
"$(ctx::fw_events_log)" "$(ctx::events_log)" \
|
|
"$(config::interface)" "$(ctx::net)" \
|
|
"$(ctx::clients)" "$(ctx::meta)" \
|
|
"$hours" "$filter_peer" "$service_ip" "$exclude_str" 2>/dev/null)
|
|
fi
|
|
|
|
local accept_data=""
|
|
if [[ "$drop_only" != "true" ]]; then
|
|
local since_arg="" ext_flag="0"
|
|
[[ "$hours" -gt 0 ]] && since_arg="${hours}h"
|
|
[[ "$external_only" == "true" ]] && ext_flag="1"
|
|
[[ -f "$(ctx::accept_events_log)" ]] && \
|
|
accept_data=$(json::accept_aggregate \
|
|
"$(ctx::accept_events_log)" "$(ctx::net)" "$(ctx::clients)" \
|
|
"$since_arg" "$filter_peer" "$ext_flag" "$exclude_str" 2>/dev/null)
|
|
fi
|
|
|
|
[[ -z "$data" && -z "$accept_data" ]] && \
|
|
log::wg_warning "No activity data found" && return 0
|
|
|
|
# ── Build accept maps ──
|
|
declare -gA _ACCEPT_PEER=()
|
|
declare -gA _ACCEPT_DEST_KEYS=()
|
|
declare -gA _ACCEPT_DEST=()
|
|
|
|
while IFS='|' read -r type rest; do
|
|
[[ -z "$type" ]] && continue
|
|
case "$type" in
|
|
peer)
|
|
local a_name a_bi a_bo a_pi a_po a_conns
|
|
IFS='|' read -r a_name a_bi a_bo a_pi a_po a_conns <<< "$rest"
|
|
_ACCEPT_PEER["$a_name"]="${a_bi}|${a_bo}|${a_pi}|${a_po}|${a_conns}"
|
|
;;
|
|
dest)
|
|
local d_peer d_ip d_port d_proto d_bytes_orig d_bytes_reply d_count
|
|
IFS='|' read -r d_peer d_ip d_port d_proto d_bytes_orig d_bytes_reply d_count <<< "$rest"
|
|
local d_key="${d_peer}:${d_ip}:${d_port}:${d_proto}"
|
|
_ACCEPT_DEST["$d_key"]="${d_bytes_orig}|${d_bytes_reply}|${d_count}"
|
|
_ACCEPT_DEST_KEYS["$d_peer"]+="${d_key} "
|
|
;;
|
|
esac
|
|
done <<< "$accept_data"
|
|
|
|
# ── Measure column widths ──
|
|
local w_peer=16 w_count=1
|
|
|
|
while IFS='|' read -r type rest; do
|
|
case "$type" in
|
|
peer)
|
|
local name drops
|
|
name=$(echo "$rest" | cut -d'|' -f1)
|
|
drops=$(echo "$rest" | cut -d'|' -f4)
|
|
(( ${#name} > w_peer )) && w_peer=${#name}
|
|
(( ${#drops} > w_count )) && w_count=${#drops}
|
|
;;
|
|
service)
|
|
local svc_count
|
|
svc_count=$(echo "$rest" | cut -d'|' -f3)
|
|
(( ${#svc_count} > w_count )) && w_count=${#svc_count}
|
|
;;
|
|
esac
|
|
done <<< "$data"
|
|
|
|
for a_name in "${!_ACCEPT_PEER[@]}"; do
|
|
(( ${#a_name} > w_peer )) && w_peer=${#a_name}
|
|
local a_conns_val="${_ACCEPT_PEER[$a_name]##*|}"
|
|
(( ${#a_conns_val} > w_count )) && w_count=${#a_conns_val}
|
|
done
|
|
for key in "${!_ACCEPT_DEST[@]}"; do
|
|
local d_val="${_ACCEPT_DEST[$key]}"
|
|
local d_count_val="${d_val##*|}"
|
|
(( ${#d_count_val} > w_count )) && w_count=${#d_count_val}
|
|
done
|
|
|
|
(( w_peer += 2 ))
|
|
local drops_col=$(( w_peer + 30 ))
|
|
|
|
local hours_display="${hours}h"
|
|
[[ "$hours" == "0" ]] && hours_display="all time"
|
|
|
|
log::section "Activity Monitor (last ${hours_display})"
|
|
echo ""
|
|
|
|
if display::is_table "activity"; then
|
|
cmd::activity::_render_table "$data"
|
|
return 0
|
|
fi
|
|
|
|
# ── Batch resolve accept dest IPs ──
|
|
declare -gA _DEST_RESOLVE_CACHE=()
|
|
local -a _dest_specs=()
|
|
for _dk in "${!_ACCEPT_DEST[@]}"; do
|
|
local _rest="${_dk#*:}"
|
|
local _dip="${_rest%%:*}"
|
|
local _pp="${_rest#*:}"
|
|
local _dport="${_pp%%:*}"
|
|
local _dproto="${_pp##*:}"
|
|
local _spec="${_dip}:${_dport}:${_dproto}"
|
|
local _found=false
|
|
for _s in "${_dest_specs[@]:-}"; do
|
|
[[ "$_s" == "$_spec" ]] && _found=true && break
|
|
done
|
|
$_found || _dest_specs+=("$_spec")
|
|
done
|
|
|
|
if [[ ${#_dest_specs[@]} -gt 0 ]]; then
|
|
while IFS='|' read -r _spec _display; do
|
|
[[ -n "$_spec" ]] && _DEST_RESOLVE_CACHE["$_spec"]="$_display"
|
|
done < <(json::batch_resolve_dest "${_dest_specs[@]}" 2>/dev/null)
|
|
fi
|
|
|
|
# ── Accept dest inline renderer ──
|
|
_render_peer_accept_dests() {
|
|
local peer_name="$1"
|
|
local keys="${_ACCEPT_DEST_KEYS[$peer_name]:-}"
|
|
[[ -z "$keys" ]] && return 0
|
|
for d_key in $keys; do
|
|
local dest_stats="${_ACCEPT_DEST[$d_key]:-}"
|
|
[[ -z "$dest_stats" ]] && continue
|
|
local d_bytes_orig d_bytes_reply d_count
|
|
IFS='|' read -r d_bytes_orig d_bytes_reply d_count <<< "$dest_stats"
|
|
local rest_key="${d_key#${peer_name}:}"
|
|
local d_ip="${rest_key%%:*}"
|
|
local pp="${rest_key#*:}"
|
|
local d_port="${pp%%:*}"
|
|
local d_proto="${pp##*:}"
|
|
local spec="${d_ip}:${d_port}:${d_proto}"
|
|
local resolved="${_DEST_RESOLVE_CACHE[$spec]:-${d_ip}:${d_port}/${d_proto}}"
|
|
local dest_display="$resolved"
|
|
if [[ "$show_ports" == "true" && "$resolved" != "${d_ip}:"* && "$resolved" != "${d_ip} "* ]]; then
|
|
if [[ -n "$d_port" && "$d_port" != "0" ]]; then
|
|
dest_display=$(printf "%s \033[2m(%s:%s)\033[0m" "$resolved" "$d_ip" "$d_port")
|
|
else
|
|
dest_display=$(printf "%s \033[2m(%s)\033[0m" "$resolved" "$d_ip")
|
|
fi
|
|
fi
|
|
ui::activity::accept_dest_row \
|
|
"$dest_display" "$d_bytes_orig" "$d_bytes_reply" \
|
|
"$d_count" "$drops_col" "$w_count"
|
|
done
|
|
}
|
|
|
|
local first_peer=true skip_peer=false current_name=""
|
|
local -a rendered_peers=()
|
|
|
|
# ── Main render loop ──
|
|
while IFS='|' read -r record_type rest; do
|
|
case "$record_type" in
|
|
peer)
|
|
local name rx tx drops
|
|
IFS='|' read -r name rx tx drops <<< "$rest"
|
|
|
|
[[ -n "$current_name" ]] && [[ "$drop_only" != "true" ]] && \
|
|
_render_peer_accept_dests "$current_name"
|
|
|
|
skip_peer=false
|
|
current_name="$name"
|
|
local has_accept="${_ACCEPT_PEER[$name]:-}"
|
|
|
|
$first_peer || echo ""
|
|
first_peer=false
|
|
rendered_peers+=("$name")
|
|
|
|
local rx_fmt tx_fmt
|
|
rx_fmt=$(fmt::bytes "$rx")
|
|
tx_fmt=$(fmt::bytes "$tx")
|
|
local name_pad rx_pad tx_pad
|
|
name_pad=$(printf "%-${w_peer}s" "$name")
|
|
rx_pad=$(printf "%-10s" "$rx_fmt")
|
|
tx_pad=$(printf "%-10s" "$tx_fmt")
|
|
|
|
local drop_word="drops"
|
|
[[ "$drops" -eq 1 ]] && drop_word="drop"
|
|
|
|
if [[ "$accept_only" == "true" ]]; then
|
|
printf " \033[1m%s\033[0m\n" "$name_pad"
|
|
else
|
|
ui::activity::peer_row \
|
|
"$name_pad" "$rx_pad" "$tx_pad" "$drops" "$drop_word" "$w_count"
|
|
fi
|
|
|
|
if [[ -n "$has_accept" && "$drop_only" != "true" ]]; then
|
|
local a_bi a_bo a_pi a_po a_conns
|
|
IFS='|' read -r a_bi a_bo a_pi a_po a_conns <<< "$has_accept"
|
|
ui::activity::accept_row \
|
|
"$name_pad" \
|
|
"$(printf '%-10s' "$(fmt::bytes "$a_bi")")" \
|
|
"$(printf '%-10s' "$(fmt::bytes "$a_bo")")" \
|
|
"$a_conns" "$w_count"
|
|
fi
|
|
;;
|
|
|
|
service)
|
|
$skip_peer && continue
|
|
[[ "$accept_only" == "true" ]] && continue
|
|
local peer dest_display dst_ip dst_port proto drop_count
|
|
IFS='|' read -r peer dest_display dst_ip dst_port proto drop_count <<< "$rest"
|
|
local svc_display="$dest_display"
|
|
if [[ "$show_ports" == "true" && -n "$dst_ip" ]]; then
|
|
if [[ -n "$dst_port" ]]; then
|
|
svc_display=$(printf "%s \033[2m(%s:%s)\033[0m" \
|
|
"$dest_display" "$dst_ip" "$dst_port")
|
|
else
|
|
svc_display=$(printf "%s \033[2m(%s)\033[0m" \
|
|
"$dest_display" "$dst_ip")
|
|
fi
|
|
fi
|
|
local svc_drop_word="drops"
|
|
[[ "$drop_count" -eq 1 ]] && svc_drop_word="drop"
|
|
ui::activity::service_row \
|
|
"$svc_display" "$drop_count" "$svc_drop_word" "$drops_col" "$w_count"
|
|
;;
|
|
esac
|
|
done <<< "$data"
|
|
|
|
[[ -n "$current_name" ]] && [[ "$drop_only" != "true" ]] && \
|
|
_render_peer_accept_dests "$current_name"
|
|
|
|
# ── Accept-only peers ──
|
|
if [[ "$drop_only" != "true" ]]; then
|
|
for a_name in $(echo "${!_ACCEPT_PEER[@]}" | tr ' ' '\n' | sort); do
|
|
local already=false
|
|
for rp in "${rendered_peers[@]:-}"; do
|
|
[[ "$rp" == "$a_name" ]] && already=true && break
|
|
done
|
|
$already && continue
|
|
|
|
$first_peer || echo ""
|
|
first_peer=false
|
|
|
|
local a_stats="${_ACCEPT_PEER[$a_name]}"
|
|
local a_bi a_bo a_pi a_po a_conns
|
|
IFS='|' read -r a_bi a_bo a_pi a_po a_conns <<< "$a_stats"
|
|
local name_pad
|
|
name_pad=$(printf "%-${w_peer}s" "$a_name")
|
|
printf " \033[1m%s\033[0m\n" "$name_pad"
|
|
ui::activity::accept_row \
|
|
"$name_pad" \
|
|
"$(printf '%-10s' "$(fmt::bytes "$a_bi")")" \
|
|
"$(printf '%-10s' "$(fmt::bytes "$a_bo")")" \
|
|
"$a_conns" "$w_count"
|
|
_render_peer_accept_dests "$a_name"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
function cmd::activity::_render_table() {
|
|
local data="${1:-}"
|
|
[[ -z "$data" ]] && return 0
|
|
ui::activity::header_table
|
|
local skip_peer=false
|
|
while IFS='|' read -r record_type rest; do
|
|
case "$record_type" in
|
|
peer)
|
|
local name rx tx drops
|
|
IFS='|' read -r name rx tx drops <<< "$rest"
|
|
skip_peer=false
|
|
local rx_fmt tx_fmt
|
|
rx_fmt=$(fmt::bytes "$rx")
|
|
tx_fmt=$(fmt::bytes "$tx")
|
|
local drop_word="drops"
|
|
[[ "$drops" -eq 1 ]] && drop_word="drop"
|
|
ui::activity::peer_row_table "$name" "$rx_fmt" "$tx_fmt" "$drops" "$drop_word"
|
|
;;
|
|
service)
|
|
$skip_peer && continue
|
|
local peer dest_display drop_count
|
|
IFS='|' read -r peer dest_display drop_count <<< "$rest"
|
|
ui::activity::service_row_table "$dest_display" "$drop_count" "drops"
|
|
;;
|
|
esac
|
|
done <<< "$data"
|
|
}
|
|
|
|
function cmd::activity::_output_json() {
|
|
local hours="${1:-24}"
|
|
local data
|
|
data=$(json::activity_aggregate \
|
|
"$(ctx::fw_events_log)" "$(ctx::events_log)" \
|
|
"$(config::interface)" "$(ctx::net)" \
|
|
"$(ctx::clients)" "$(ctx::meta)" \
|
|
"$hours" "" "" "" 2>/dev/null)
|
|
local -a peers=()
|
|
while IFS='|' read -r record_type rest; do
|
|
[[ "$record_type" != "peer" ]] && continue
|
|
local name rx tx drops
|
|
IFS='|' read -r name rx tx drops <<< "$rest"
|
|
peers+=("{\"name\":\"${name}\",\"rx\":${rx},\"tx\":${tx},\"drops\":${drops}}")
|
|
done <<< "$data"
|
|
local array
|
|
array=$(IFS=','; echo "${peers[*]:-}")
|
|
printf '[%s]' "$array" | json::envelope "activity" "${#peers[@]}"
|
|
} |