110 lines
4.1 KiB
Bash
110 lines
4.1 KiB
Bash
|
#!/bin/bash
|
||
|
|
||
|
# network_traffic.sh [-tPOLLING_INTERVAL] [NETWORK_INTERFACE...]
|
||
|
|
||
|
getopts t: __ && shift
|
||
|
isecs=${OPTARG:-1}
|
||
|
ifaces=($@)
|
||
|
: ${rate_max:=1000000} # maximum transfer rate for {percent}, can be overridden setting the env var
|
||
|
|
||
|
# `snore` adapted from https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever
|
||
|
# without MacOS workaround, TODO: with _snore_fd initialized separatedly, also i dont touch IFS so dont bother with it
|
||
|
snore() {
|
||
|
local IFS
|
||
|
[[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null
|
||
|
read ${1:+-t "$1"} -u $_snore_fd || :
|
||
|
}
|
||
|
|
||
|
human_readable() {
|
||
|
local hrunits=( B K M G T P )
|
||
|
local ndigits=${#1}
|
||
|
local idxunit=$(( (2 + ndigits) / 3 - 1))
|
||
|
local lentrim=$(( ndigits - (idxunit * 3 ) ))
|
||
|
echo ${1::$lentrim}${hrunits[$idxunit]}
|
||
|
}
|
||
|
|
||
|
exit_err() {
|
||
|
printf '{"text": "⚠ %s", "tooltip": "%s", "class": "error"}\n' "$@"
|
||
|
exit
|
||
|
}
|
||
|
|
||
|
if test ${#ifaces[@]} -gt 0; then
|
||
|
# sanity check the interface names
|
||
|
for iface in ${ifaces[@]}; do
|
||
|
test -h "/sys/class/net/${iface}" || exit_err "${iface}" "${iface} is not an existing network interface name"
|
||
|
done
|
||
|
else
|
||
|
# default to all interfaces except `lo`
|
||
|
ifaces=(/sys/class/net/*)
|
||
|
ifaces=(${ifaces[@]##*/})
|
||
|
ifaces=(${ifaces[@]//lo/})
|
||
|
# TODO: check that filtering out `lo` is enough, else `^(eth|wlan|enp|wlp)` as suggested
|
||
|
fi
|
||
|
|
||
|
# sanity check polling interval
|
||
|
if test ${isecs} -lt 1; then
|
||
|
exit_err "${isecs}" "${isecs} is not a valid polling interval"
|
||
|
fi
|
||
|
# NOTE: `snore` would take a decimal interval but bash arithmetic does not
|
||
|
#if test $(echo "${isecs} >= 0.2" |bc) -eq 0; then
|
||
|
# exit_err "${isecs}" "${isecs} is not a valid polling interval"
|
||
|
#fi
|
||
|
|
||
|
# NOTE: `/proc/net/dev` format is:
|
||
|
# interface:
|
||
|
# RX bytes packets errs drop fifo frame compressed multicast
|
||
|
# TX bytes packets errs drop fifo colls carrier compressed
|
||
|
|
||
|
# NOTE: array items are:
|
||
|
# 0=rx_bytes 1=rx_packets 2=rx_errs 3=rx_drop
|
||
|
# 4=tx_bytes 5=tx_packets 6=tx_errs 7=tx_drop
|
||
|
for iface in ${ifaces[@]} aggregate; do
|
||
|
declare -a traffic_prev_${iface} traffic_curr_${iface} traffic_delt_${iface}
|
||
|
declare -n traffic_prev=traffic_prev_${iface}
|
||
|
declare -n traffic_curr=traffic_curr_${iface}
|
||
|
declare -n traffic_delt=traffic_delt_${iface}
|
||
|
traffic_prev=( 0 0 0 0 0 0 0 0 )
|
||
|
traffic_curr=( 0 0 0 0 0 0 0 0 )
|
||
|
traffic_delt=( 0 0 0 0 0 0 0 0 )
|
||
|
done
|
||
|
|
||
|
# TODO: rearrange the loop, do not show bogus on first iteration
|
||
|
while snore ${isecs} ;do
|
||
|
tooltip=""
|
||
|
traffic_delt_aggregate=( 0 0 0 0 0 0 0 0 )
|
||
|
|
||
|
readarray -s2 proc_net_dev </proc/net/dev
|
||
|
while read -a data; do
|
||
|
iface=${data[0]%:}
|
||
|
test "${ifaces[*]}" = "${ifaces[*]//${iface}/}" && continue
|
||
|
declare -n traffic_prev=traffic_prev_${iface}
|
||
|
declare -n traffic_curr=traffic_curr_${iface}
|
||
|
declare -n traffic_delt=traffic_delt_${iface}
|
||
|
traffic_curr=(${data[@]:1:4} ${data[@]:9:4})
|
||
|
#FIXME: tooltip is delayed one iteration wrt main display (but why?)
|
||
|
#printf -v tooltip_hr_rx '%4s⇣' $(human_readable ${traffic_delt[0]})
|
||
|
#printf -v tooltip_hr_tx '%4s⇡' $(human_readable ${traffic_delt[4]})
|
||
|
#tooltip="${tooltip:+${tooltip}\r}${iface}\t${tooltip_hr_rx}\t${tooltip_hr_tx}"
|
||
|
for i in {0..7}; do
|
||
|
(( traffic_delt[i] = ( traffic_curr[i] - traffic_prev[i] ) / isecs ))
|
||
|
(( traffic_delt_aggregate[i] += traffic_delt[i] ))
|
||
|
done
|
||
|
traffic_prev=(${traffic_curr[@]})
|
||
|
done <<<"${proc_net_dev[@]}"
|
||
|
|
||
|
printf '{"text": "%4s⇣ %4s⇡", "tooltip": "%s", "percentage": %d}\n' \
|
||
|
$(human_readable ${traffic_delt_aggregate[0]}) \
|
||
|
$(human_readable ${traffic_delt_aggregate[4]}) \
|
||
|
"${tooltip}" \
|
||
|
$(( ( traffic_delt_aggregate[0] + traffic_delt_aggregate[4] ) / rate_max ))
|
||
|
#printf '{"text": "%5s⇣ %5s⇡", "alt": "%s", "tooltip": "%s", "class": "%s", "percentage": %d }\n' $(human_readable ${traffic_delt[0]}) $(human_readable ${traffic_delt[4]}) '_alt' '_tooltip' '_class' 0
|
||
|
done
|
||
|
|
||
|
|
||
|
# NOTE: in waybar config (do NOT use "interval"):
|
||
|
# "custom/network_traffic": {
|
||
|
# "exec": "~/.config/waybar/scripts/network_traffic.sh",
|
||
|
# "return-type": "json",
|
||
|
# "format": "Speed: {}", // optional
|
||
|
# },
|