mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-04 09:53:56 +08:00
🎉 Sync 2026-09-27 21:03:26
This commit is contained in:
1 parent
f4559bbed1
commit
065763f7fd
673 files changed
+261285
-34160
No files matched your search
+283
@@ -0,0 +1,283 @@
|
||||
#!/bin/sh
|
||||
# shellcheck shell=ash
|
||||
|
||||
# Variables are populated by sourced SafeShield helpers and ss_load_config().
|
||||
# shellcheck disable=SC2154
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
umask 077
|
||||
|
||||
SS_STATSD_FUNCTIONS_LIB="${SS_STATSD_FUNCTIONS_LIB:-/lib/functions.sh}"
|
||||
SS_STATSD_CORE_LIB="${SS_STATSD_CORE_LIB:-/usr/lib/safeshield/core.sh}"
|
||||
SS_STATSD_AWK_DIR="${SS_STATSD_AWK_DIR:-/usr/lib/safeshield/statistics}"
|
||||
SS_STATSD_POLL_COMMAND="${SS_STATSD_POLL_COMMAND:-/usr/libexec/safeshield-stats-poll}"
|
||||
SS_STATSD_IDENTITY_COMMAND="${SS_STATSD_IDENTITY_COMMAND:-/usr/libexec/safeshield-stats-identities}"
|
||||
SS_STATSD_PERSISTENT_STATE_FILE="${SS_STATSD_PERSISTENT_STATE_FILE:-/etc/safeshield/statistics-state.tsv}"
|
||||
SS_STATSD_PERSISTENT_JOURNAL_FILE="${SS_STATSD_PERSISTENT_JOURNAL_FILE:-/etc/safeshield/statistics-journal.tsv}"
|
||||
SS_STATSD_SEQUENCE_FILE="${SS_STATSD_SEQUENCE_FILE:-/etc/safeshield/statistics-sequence.tsv}"
|
||||
SS_STATSD_SEQUENCE_RESERVATION_SIZE="${SS_STATSD_SEQUENCE_RESERVATION_SIZE:-4096}"
|
||||
SS_STATSD_UPLOAD_WINDOW_HOURS="${SS_STATSD_UPLOAD_WINDOW_HOURS:-2}"
|
||||
SS_STATSD_PERSISTENT_INTERVAL_S="${SS_STATSD_PERSISTENT_INTERVAL_S:-3600}"
|
||||
SS_STATSD_PERSISTENT_COMPACT_INTERVAL_S="${SS_STATSD_PERSISTENT_COMPACT_INTERVAL_S:-604800}"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
. "$SS_STATSD_FUNCTIONS_LIB"
|
||||
# shellcheck disable=SC1090
|
||||
. "$SS_STATSD_CORE_LIB"
|
||||
|
||||
stats_poller_pid=''
|
||||
stats_awk_pid=''
|
||||
stats_fifo=''
|
||||
stats_lock_dir=''
|
||||
stats_lock_owned=0
|
||||
|
||||
stats_new_generation_id() {
|
||||
local generation_id="${SS_STATSD_GENERATION_ID:-}"
|
||||
|
||||
if [ -n "$generation_id" ]; then
|
||||
printf '%s\n' "$generation_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -r /proc/sys/kernel/random/uuid ]; then
|
||||
IFS= read -r generation_id </proc/sys/kernel/random/uuid || generation_id=''
|
||||
fi
|
||||
|
||||
if [ -z "$generation_id" ]; then
|
||||
generation_id="fallback-$(date +%s 2>/dev/null || printf '0')-$$"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$generation_id"
|
||||
}
|
||||
|
||||
stats_process_running() {
|
||||
local pid="${1:-}"
|
||||
[ -n "$pid" ] || return 1
|
||||
kill -0 "$pid" 2>/dev/null
|
||||
}
|
||||
|
||||
stats_cleanup() {
|
||||
trap - EXIT HUP INT TERM
|
||||
|
||||
# Stop the low-frequency UBus poller first. awk then receives EOF and runs
|
||||
# its END block, which performs the final snapshot and persistence flush.
|
||||
if stats_process_running "$stats_poller_pid"; then
|
||||
kill "$stats_poller_pid" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
[ -n "$stats_poller_pid" ] && wait "$stats_poller_pid" 2>/dev/null || true
|
||||
stats_poller_pid=''
|
||||
|
||||
[ -n "$stats_awk_pid" ] && wait "$stats_awk_pid" 2>/dev/null || true
|
||||
stats_awk_pid=''
|
||||
|
||||
[ -n "$stats_fifo" ] && rm -f "$stats_fifo"
|
||||
|
||||
if [ "$stats_lock_owned" -eq 1 ] && [ -n "$stats_lock_dir" ]; then
|
||||
rm -rf "$stats_lock_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
stats_terminate() {
|
||||
local rc="${1:-0}"
|
||||
stats_cleanup
|
||||
exit "$rc"
|
||||
}
|
||||
|
||||
stats_acquire_lock() {
|
||||
local owner_pid=''
|
||||
|
||||
stats_lock_dir="${SS_STATISTICS_DIR}/collector.lock"
|
||||
|
||||
if mkdir "$stats_lock_dir" 2>/dev/null; then
|
||||
stats_lock_owned=1
|
||||
printf '%s\n' "$$" >"${stats_lock_dir}/pid"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -r "${stats_lock_dir}/pid" ]; then
|
||||
owner_pid="$(cat "${stats_lock_dir}/pid" 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if stats_process_running "$owner_pid"; then
|
||||
log_warn "Statistics collector is already running (pid=${owner_pid})"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_warn 'Removing stale statistics collector lock'
|
||||
rm -rf "$stats_lock_dir" || return 1
|
||||
mkdir "$stats_lock_dir" || return 1
|
||||
stats_lock_owned=1
|
||||
printf '%s\n' "$$" >"${stats_lock_dir}/pid"
|
||||
return 0
|
||||
}
|
||||
|
||||
stats_poll_loop() {
|
||||
local snapshot_interval="$1"
|
||||
local poll_command="$2"
|
||||
local sleep_pid=''
|
||||
|
||||
trap 'if [ -n "$sleep_pid" ]; then kill "$sleep_pid" 2>/dev/null || true; fi; exit 0' HUP INT TERM
|
||||
|
||||
while :; do
|
||||
if ! "$poll_command"; then
|
||||
printf 'error\tdnsmasq_ubus_poll_failed\n'
|
||||
fi
|
||||
|
||||
sleep "$snapshot_interval" &
|
||||
sleep_pid=$!
|
||||
wait "$sleep_pid" 2>/dev/null || true
|
||||
sleep_pid=''
|
||||
done
|
||||
}
|
||||
|
||||
stats_run_collector() {
|
||||
local snapshot_interval="$1"
|
||||
local retention_hours="$2"
|
||||
local lease_file="$3"
|
||||
local persistent_state_file="$4"
|
||||
local persistent_journal_file="$5"
|
||||
local sequence_file="$6"
|
||||
local generation_id ipv6_neigh_command='' ipv6_identity_command='' ipv6_identity_file="${SS_STATSD_IPV6_IDENTITY_FILE:-}" force_rebaseline=0 rc=0
|
||||
|
||||
if [ -f "${SS_STATISTICS_REBASELINE_FILE}" ]; then
|
||||
force_rebaseline=1
|
||||
fi
|
||||
|
||||
# This is only a candidate for a brand-new dataset. The collector modules keep
|
||||
# the generation restored from tmpfs or persistent state when one already exists.
|
||||
generation_id="$(stats_new_generation_id)"
|
||||
if command_exists ip; then
|
||||
ipv6_neigh_command='ip -6 neigh show 2>/dev/null'
|
||||
fi
|
||||
if [ -z "$ipv6_identity_file" ] && [ -x "$SS_STATSD_IDENTITY_COMMAND" ]; then
|
||||
ipv6_identity_command="$SS_STATSD_IDENTITY_COMMAND 2>/dev/null"
|
||||
fi
|
||||
|
||||
stats_fifo="${SS_STATISTICS_DIR}/events.$$"
|
||||
rm -f "$stats_fifo"
|
||||
mkfifo "$stats_fifo" || {
|
||||
log_error "Failed to create statistics FIFO: ${stats_fifo}"
|
||||
return 1
|
||||
}
|
||||
|
||||
stats_poll_loop "$snapshot_interval" "$SS_STATSD_POLL_COMMAND" >"$stats_fifo" &
|
||||
stats_poller_pid=$!
|
||||
|
||||
awk \
|
||||
-v state_file="${SS_STATISTICS_STATE_FILE}" \
|
||||
-v json_file="${SS_STATISTICS_JSON_FILE}" \
|
||||
-v upload_json_file="${SS_STATISTICS_UPLOAD_JSON_FILE}" \
|
||||
-v upload_window_hours="${SS_STATSD_UPLOAD_WINDOW_HOURS}" \
|
||||
-v persistent_state_file="${persistent_state_file}" \
|
||||
-v persistent_journal_file="${persistent_journal_file}" \
|
||||
-v sequence_file="${sequence_file}" \
|
||||
-v snapshot_seq_reservation_size="${SS_STATSD_SEQUENCE_RESERVATION_SIZE}" \
|
||||
-v persistent_interval="${SS_STATSD_PERSISTENT_INTERVAL_S}" \
|
||||
-v persistent_compact_interval="${SS_STATSD_PERSISTENT_COMPACT_INTERVAL_S}" \
|
||||
-v snapshot_interval="${snapshot_interval}" \
|
||||
-v retention_hours="${retention_hours}" \
|
||||
-v lease_file="${lease_file}" \
|
||||
-v ipv6_neigh_command="${ipv6_neigh_command}" \
|
||||
-v ipv6_identity_file="${ipv6_identity_file}" \
|
||||
-v ipv6_identity_command="${ipv6_identity_command}" \
|
||||
-v lease_refresh_interval=60 \
|
||||
-v identity_cache_ttl=60 \
|
||||
-v max_devices=128 \
|
||||
-v generation_seed="${generation_id}" \
|
||||
-v force_rebaseline="${force_rebaseline}" \
|
||||
-v rebaseline_file="${SS_STATISTICS_REBASELINE_FILE}" \
|
||||
-f "$SS_STATSD_AWK_DIR/00-common.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/10-recovery.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/20-identity.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/30-aggregate.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/40-persistence.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/50-output.awk" \
|
||||
-f "$SS_STATSD_AWK_DIR/90-main.awk" \
|
||||
<"$stats_fifo" &
|
||||
stats_awk_pid=$!
|
||||
|
||||
wait "$stats_awk_pid" || rc=$?
|
||||
stats_awk_pid=''
|
||||
|
||||
if stats_process_running "$stats_poller_pid"; then
|
||||
kill "$stats_poller_pid" 2>/dev/null || true
|
||||
fi
|
||||
wait "$stats_poller_pid" 2>/dev/null || true
|
||||
stats_poller_pid=''
|
||||
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
main() {
|
||||
local snapshot_interval configured_snapshot_interval retention_hours lease_file configured_lease_file
|
||||
local persistent_state_file persistent_journal_file sequence_file persistence_mode rc=0
|
||||
|
||||
ss_load_config || return 1
|
||||
[ "${ss_enabled}" = "1" ] || return 0
|
||||
[ "${ss_statistics_enabled}" = "1" ] || return 0
|
||||
|
||||
if ! ss_dnsmasq_safeshield_block_supported; then
|
||||
log_warn 'SafeShield statistics collector is disabled because dnsmasq does not provide the SafeShield extension'
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ -x "$SS_STATSD_POLL_COMMAND" ] || {
|
||||
log_error "SafeShield statistics poll command is unavailable: ${SS_STATSD_POLL_COMMAND}"
|
||||
return 1
|
||||
}
|
||||
command_exists mkfifo || {
|
||||
log_error 'mkfifo is required for SafeShield statistics'
|
||||
return 1
|
||||
}
|
||||
|
||||
persistent_state_file="${SS_STATSD_PERSISTENT_STATE_FILE}"
|
||||
persistent_journal_file="${SS_STATSD_PERSISTENT_JOURNAL_FILE}"
|
||||
sequence_file="${SS_STATSD_SEQUENCE_FILE}"
|
||||
persistence_mode='journal'
|
||||
if ! ss_statistics_persistence_enabled; then
|
||||
persistent_state_file=''
|
||||
persistent_journal_file=''
|
||||
sequence_file=''
|
||||
persistence_mode='volatile'
|
||||
fi
|
||||
|
||||
mkdir -p "${SS_STATISTICS_DIR}" || return 1
|
||||
if [ -n "$persistent_state_file" ]; then
|
||||
mkdir -p "${persistent_state_file%/*}" || return 1
|
||||
fi
|
||||
if [ -n "$persistent_journal_file" ]; then
|
||||
mkdir -p "${persistent_journal_file%/*}" || return 1
|
||||
fi
|
||||
if [ -n "$sequence_file" ]; then
|
||||
mkdir -p "${sequence_file%/*}" || return 1
|
||||
fi
|
||||
stats_acquire_lock || return 0
|
||||
|
||||
trap 'stats_cleanup' EXIT
|
||||
trap 'stats_terminate 129' HUP
|
||||
trap 'stats_terminate 130' INT
|
||||
trap 'stats_terminate 143' TERM
|
||||
|
||||
configured_snapshot_interval="${ss_statistics_snapshot_interval_s}"
|
||||
snapshot_interval="$(ss_statistics_effective_snapshot_interval "$configured_snapshot_interval")"
|
||||
retention_hours="${ss_statistics_retention_hours}"
|
||||
lease_file="${SS_STATSD_DHCP_LEASE_FILE:-/tmp/dhcp.leases}"
|
||||
if [ -z "${SS_STATSD_DHCP_LEASE_FILE:-}" ] && command_exists uci; then
|
||||
configured_lease_file="$(uci -q get dhcp.@dnsmasq[0].leasefile 2>/dev/null || true)"
|
||||
[ -n "$configured_lease_file" ] && lease_file="$configured_lease_file"
|
||||
fi
|
||||
|
||||
log_info "Starting statistics collector (source=dnsmasq-ubus snapshot=${snapshot_interval}s configured_snapshot=${configured_snapshot_interval}s retention=${retention_hours}h persistence=${persistence_mode} journal=${SS_STATSD_PERSISTENT_INTERVAL_S}s compact=${SS_STATSD_PERSISTENT_COMPACT_INTERVAL_S}s)"
|
||||
|
||||
stats_run_collector \
|
||||
"$snapshot_interval" \
|
||||
"$retention_hours" \
|
||||
"$lease_file" \
|
||||
"$persistent_state_file" \
|
||||
"$persistent_journal_file" \
|
||||
"$sequence_file" || rc=$?
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in new issue
Block a user