131 lines
3.1 KiB
Bash
Executable File
131 lines
3.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Measure live internet speed for one direction
|
|
# omarchy:group=network
|
|
# omarchy:args=[down|up]
|
|
|
|
set -e
|
|
|
|
direction="${1:-}"
|
|
probe=1.1.1.1
|
|
parallel=8
|
|
url_count=3
|
|
|
|
case "$direction" in
|
|
down | up)
|
|
;;
|
|
*)
|
|
echo "Usage: omarchy-network-speedtest [down|up]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
format_mbps() {
|
|
awk -v value="$1" 'BEGIN {
|
|
if (value <= 0) print "0.0"
|
|
else if (value < 10) printf "%.1f\n", value
|
|
else printf "%.0f\n", value
|
|
}'
|
|
}
|
|
|
|
iface=$(ip route get "$probe" 2>/dev/null | awk '{ for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }')
|
|
|
|
if [[ -z $iface || ! -r /sys/class/net/$iface/statistics/rx_bytes || ! -r /sys/class/net/$iface/statistics/tx_bytes ]]; then
|
|
echo "No active network interface" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! omarchy-cmd-present curl; then
|
|
echo "curl is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
fast_token="YXNkZmFzZGxmbnNkYWZoYXNkZmhrYWxm"
|
|
fast_api_url="https://api.fast.com/netflix/speedtest/v2?https=true&token=$fast_token&urlCount=$url_count"
|
|
|
|
fast_urls=$(curl -fsS "$fast_api_url" 2>/dev/null | jq -r '.targets[]?.url // empty')
|
|
|
|
if [[ -z $fast_urls ]]; then
|
|
echo "Failed to fetch speed test endpoints" >&2
|
|
exit 1
|
|
fi
|
|
|
|
traffic_pids=()
|
|
|
|
cleanup() {
|
|
local pid
|
|
for pid in "${traffic_pids[@]}"; do
|
|
[[ -n $pid ]] || continue
|
|
pkill -TERM -P "$pid" 2>/dev/null || true
|
|
kill "$pid" 2>/dev/null || true
|
|
done
|
|
for pid in "${traffic_pids[@]}"; do
|
|
[[ -n $pid ]] || continue
|
|
wait "$pid" 2>/dev/null || true
|
|
done
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Round-robin across the returned URLs so we spread load across Netflix OCA nodes.
|
|
traffic_worker() {
|
|
local urls=("$@")
|
|
local url_count=${#urls[@]}
|
|
local idx=$RANDOM
|
|
if [[ $direction == "down" ]]; then
|
|
while true; do
|
|
url=${urls[$((idx % url_count))]}
|
|
curl -fsS -o /dev/null "$url" 2>/dev/null || return
|
|
idx=$((idx + 1))
|
|
done
|
|
else
|
|
while true; do
|
|
url=${urls[$((idx % url_count))]}
|
|
dd if=/dev/zero bs=1M count=64 2>/dev/null | curl -fsS -o /dev/null -X POST --data-binary @- "$url" 2>/dev/null || return
|
|
idx=$((idx + 1))
|
|
done
|
|
fi
|
|
}
|
|
|
|
for (( i = 0; i < parallel; i++ )); do
|
|
traffic_worker $fast_urls &
|
|
traffic_pids+=("$!")
|
|
done
|
|
|
|
rx_before=$(cat "/sys/class/net/$iface/statistics/rx_bytes")
|
|
tx_before=$(cat "/sys/class/net/$iface/statistics/tx_bytes")
|
|
|
|
any_alive() {
|
|
local pid
|
|
for pid in "${traffic_pids[@]}"; do
|
|
[[ -n $pid ]] || continue
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
while any_alive; do
|
|
sleep 1
|
|
rx_after=$(cat "/sys/class/net/$iface/statistics/rx_bytes")
|
|
tx_after=$(cat "/sys/class/net/$iface/statistics/tx_bytes")
|
|
|
|
if [[ $direction == "down" ]]; then
|
|
rate=$(awk -v before="$rx_before" -v after="$rx_after" 'BEGIN {
|
|
if (after < before) print 0
|
|
else print (after - before) * 8 / 1000000
|
|
}')
|
|
else
|
|
rate=$(awk -v before="$tx_before" -v after="$tx_after" 'BEGIN {
|
|
if (after < before) print 0
|
|
else print (after - before) * 8 / 1000000
|
|
}')
|
|
fi
|
|
|
|
format_mbps "$rate"
|
|
rx_before=$rx_after
|
|
tx_before=$tx_after
|
|
done
|
|
|
|
wait 2>/dev/null || true
|