206 lines
7.0 KiB
Bash
Executable File
206 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# fleet-beacon.sh — Multi-node Realtime Telemetry Beacon (Pure Bash Fallback)
|
|
# Observatoř Telemetry Platform // Fleet Operations
|
|
#
|
|
# Broadcasts lightweight UDP telemetry packets (port 9850) every 1.5s.
|
|
# Zero external dependencies, pure coreutils + bash.
|
|
# =============================================================================
|
|
|
|
set -e
|
|
|
|
PORT=9850
|
|
INTERVAL=1.5
|
|
TARGETS=(
|
|
"255.255.255.255"
|
|
"192.168.1.255"
|
|
"192.168.1.100"
|
|
"192.168.1.102"
|
|
"192.168.1.104"
|
|
"192.168.1.101"
|
|
"192.168.1.105"
|
|
"192.168.1.103"
|
|
"10.39.9.255"
|
|
"10.39.9.1"
|
|
"10.0.0.255"
|
|
"10.0.0.18"
|
|
"10.0.0.14"
|
|
"91.139.118.43"
|
|
"192.168.43.255"
|
|
"192.168.43.1"
|
|
"127.0.0.1"
|
|
)
|
|
|
|
# Identity resolution
|
|
if [ -z "$FLEET_NODE" ] || [ -z "$FLEET_ARCH" ]; then
|
|
[ -f "/etc/fleet_id" ] && source "/etc/fleet_id" 2>/dev/null || true
|
|
[ -f "$HOME/.fleet_id" ] && source "$HOME/.fleet_id" 2>/dev/null || true
|
|
fi
|
|
|
|
RAW_HOST="$(hostname -s 2>/dev/null || uname -n)"
|
|
if [ -z "$FLEET_NODE" ]; then
|
|
if [ -d "/data/data/com.termux" ] || [ -n "$TERMUX_VERSION" ] || [ "$RAW_HOST" = "localhost" ] || [ "$RAW_HOST" = "android" ]; then
|
|
FLEET_NODE="android"
|
|
elif [ "$RAW_HOST" = "penguin" ]; then
|
|
FLEET_NODE="crostini"
|
|
else
|
|
FLEET_NODE="$RAW_HOST"
|
|
fi
|
|
fi
|
|
FLEET_ARCH="${FLEET_ARCH:-$(uname -m)}"
|
|
FLEET_SIGN="${FLEET_SIGN:-[${FLEET_NODE}:${FLEET_ARCH}]}"
|
|
|
|
RUN_ONCE=0
|
|
PRINT_JSON=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--once|-1) RUN_ONCE=1 ;;
|
|
--json|-j) RUN_ONCE=1; PRINT_JSON=1 ;;
|
|
esac
|
|
done
|
|
|
|
if [ "$PRINT_JSON" -eq 0 ]; then
|
|
echo "[fleet-beacon-sh] Starting Bash telemetry beacon on $FLEET_NODE $FLEET_SIGN (port $PORT)..."
|
|
fi
|
|
|
|
prev_work=0
|
|
prev_total=0
|
|
|
|
while true; do
|
|
# 1. CPU
|
|
cpu_pct=0.0
|
|
if [ -r /proc/stat ]; then
|
|
read -r _ u n s i io irq sirq stl _ < /proc/stat 2>/dev/null || true
|
|
work=$(( u + n + s + irq + sirq + stl ))
|
|
total=$(( work + i + io ))
|
|
if [ "$prev_total" -gt 0 ] && [ "$total" -gt "$prev_total" ]; then
|
|
d_work=$(( work - prev_work ))
|
|
d_total=$(( total - prev_total ))
|
|
if [ "$d_total" -gt 0 ]; then
|
|
cpu_pct=$(awk -v w="$d_work" -v t="$d_total" 'BEGIN { printf "%.1f", (w/t)*100 }')
|
|
fi
|
|
fi
|
|
prev_work=$work
|
|
prev_total=$total
|
|
fi
|
|
|
|
# 2. RAM
|
|
mem_used=0.0
|
|
mem_total=0.0
|
|
if [ -r /proc/meminfo ]; then
|
|
tot_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
|
|
avail_kb=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
|
|
if [ -z "$avail_kb" ]; then
|
|
free_kb=$(awk '/^MemFree:/ {print $2}' /proc/meminfo)
|
|
buf_kb=$(awk '/^Buffers:/ {print $2}' /proc/meminfo)
|
|
cache_kb=$(awk '/^Cached:/ {print $2}' /proc/meminfo)
|
|
avail_kb=$(( free_kb + buf_kb + cache_kb ))
|
|
fi
|
|
if [ -n "$tot_kb" ] && [ "$tot_kb" -gt 0 ]; then
|
|
mem_total=$(awk -v t="$tot_kb" 'BEGIN { printf "%.1f", t/1048576 }')
|
|
used_kb=$(( tot_kb - avail_kb ))
|
|
mem_used=$(awk -v u="$used_kb" 'BEGIN { printf "%.1f", u/1048576 }')
|
|
fi
|
|
fi
|
|
|
|
# 3. Disk
|
|
disk_mount="/"
|
|
if [ "$FLEET_NODE" = "boxC" ] && [ -d "/mnt/kof17" ]; then
|
|
disk_mount="/mnt/kof17"
|
|
elif [ "$FLEET_NODE" = "bazina" ] && [ -d "/mnt/ssd" ]; then
|
|
disk_mount="/mnt/ssd"
|
|
fi
|
|
df_out=$(df -B1G "$disk_mount" 2>/dev/null | tail -n 1) || df_out=""
|
|
disk_total=$(echo "$df_out" | awk '{print $2}')
|
|
disk_free=$(echo "$df_out" | awk '{print $4}')
|
|
disk_total="${disk_total:-0.0}"
|
|
disk_free="${disk_free:-0.0}"
|
|
|
|
# 4. GPU (sysfs)
|
|
gpu_str=""
|
|
for gpath in /sys/class/drm/card*/device/gpu_busy_percent; do
|
|
if [ -r "$gpath" ]; then
|
|
gpu_val=$(cat "$gpath" 2>/dev/null || echo "")
|
|
[ -n "$gpu_val" ] && gpu_str="Navi10 ${gpu_val}%" && break
|
|
fi
|
|
done
|
|
|
|
# 5. Temp
|
|
temp_c=0
|
|
for tpath in /sys/class/thermal/thermal_zone0/temp /sys/class/hwmon/hwmon0/temp1_input; do
|
|
if [ -r "$tpath" ]; then
|
|
traw=$(cat "$tpath" 2>/dev/null || echo 0)
|
|
[ "$traw" -gt 0 ] && temp_c=$(( traw / 1000 )) && break
|
|
fi
|
|
done
|
|
|
|
# 6. Battery
|
|
bat_pct=-1
|
|
for bpath in /sys/class/power_supply/BAT*/capacity /sys/class/power_supply/*battery*/capacity; do
|
|
if [ -r "$bpath" ]; then
|
|
bval=$(cat "$bpath" 2>/dev/null || echo -1)
|
|
[ "$bval" -ge 0 ] && bat_pct=$bval && break
|
|
fi
|
|
done
|
|
|
|
# 7. Alert
|
|
alert=""
|
|
if [ "$FLEET_NODE" = "boxC" ] && [ "$disk_mount" = "/mnt/kof17" ]; then
|
|
awk_crit=$(awk -v df="$disk_free" 'BEGIN { print (df < 3.0) ? 1 : 0 }')
|
|
[ "$awk_crit" -eq 1 ] && alert="kof17 CRITICAL: ${disk_free}G free"
|
|
elif [ "$FLEET_NODE" = "crostini" ]; then
|
|
awk_crit=$(awk -v df="$disk_free" 'BEGIN { print (df < 1.0) ? 1 : 0 }')
|
|
[ "$awk_crit" -eq 1 ] && alert="crostini LOW DISK: ${disk_free}G free"
|
|
fi
|
|
if [ "$bat_pct" -ge 0 ] && [ "$bat_pct" -le 15 ]; then
|
|
alert="$FLEET_NODE BATTERY CRITICAL: ${bat_pct}%"
|
|
fi
|
|
|
|
ts=$(date +%s)
|
|
payload=$(printf '{"name":"%s","hostname":"%s","node":"%s","arch":"%s","sign":"%s","timestamp":%s,"cpu_percent":%s,"mem_used_gb":%s,"mem_total_gb":%s,"disk_mount":"%s","disk_free_gb":%s,"disk_total_gb":%s,"gpu_str":"%s","temp_c":%d,"battery_percent":%d,"alert":"%s"}' \
|
|
"$FLEET_NODE" "$RAW_HOST" "$FLEET_NODE" "$FLEET_ARCH" "$FLEET_SIGN" \
|
|
"$ts" "$cpu_pct" "$mem_used" "$mem_total" \
|
|
"$disk_mount" "$disk_free" "$disk_total" \
|
|
"$gpu_str" "$temp_c" "$bat_pct" "$alert")
|
|
|
|
# Broadcast UDP
|
|
for target in "${TARGETS[@]}"; do
|
|
if [ -w /dev/udp ]; then
|
|
echo -n "$payload" > "/dev/udp/$target/$PORT" 2>/dev/null || true
|
|
elif command -v socat >/dev/null 2>&1; then
|
|
echo -n "$payload" | socat - "UDP-DATAGRAM:$target:$PORT,broadcast" 2>/dev/null || true
|
|
elif command -v nc >/dev/null 2>&1; then
|
|
echo -n "$payload" | nc -u -w1 "$target" "$PORT" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
# Update SHM cache
|
|
shm_fleet="/dev/shm/fleet_telemetry.json"
|
|
tmp_path="${shm_fleet}.tmp.$$"
|
|
if [ -f "$shm_fleet" ]; then
|
|
# Merge using awk/sed
|
|
grep -v "\"${FLEET_NODE}\":" "$shm_fleet" 2>/dev/null | sed -e 's/^}$//' > "$tmp_path" || true
|
|
echo " ,\"${FLEET_NODE}\": $payload" >> "$tmp_path"
|
|
echo "}" >> "$tmp_path"
|
|
else
|
|
printf '{\n "%s": %s\n}\n' "$FLEET_NODE" "$payload" > "$tmp_path"
|
|
fi
|
|
chmod 666 "$tmp_path" 2>/dev/null || true
|
|
mv -f "$tmp_path" "$shm_fleet" 2>/dev/null || true
|
|
|
|
# WAN HTTP fallback
|
|
if [ "$FLEET_NODE" != "boxC" ]; then
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -s -m 1.5 -X POST "http://91.139.118.43/telemetry.php" \
|
|
-H "Content-Type: application/json" -d "$payload" >/dev/null 2>&1 || true
|
|
fi
|
|
fi
|
|
|
|
if [ "$PRINT_JSON" -eq 1 ]; then
|
|
echo "$payload"
|
|
fi
|
|
|
|
[ "$RUN_ONCE" -eq 1 ] && break
|
|
sleep "$INTERVAL"
|
|
done
|