James Andariese
aaa4cd251d
args were broken due to shift and other problems -1 didn't work because of the subshell output filtering didn't work with uniq. this was resolved by adding an unbuf function which uses stdbuf to fix buffering.
166 lines
4.1 KiB
Bash
166 lines
4.1 KiB
Bash
# shellcheck shell=bash
|
|
|
|
export ZERO="$0"
|
|
HELP="$ZERO: tracker [-1] [-v [-v ...]] [q [-q ...]]
|
|
-1 run once and exit
|
|
-v increase verbosity (default >= warn)
|
|
-q decrease verbosity
|
|
"
|
|
trap 'kill -15 $$' INT
|
|
|
|
trace() { (( LOG_LEVEL > 2 )) && (exec 1>&2; builtin echo "$@"|grep .||true); }
|
|
debug() { (( LOG_LEVEL > 1 )) && (exec 1>&2; builtin echo "$@"|grep .||true); }
|
|
info() { (( LOG_LEVEL > 0 )) && (exec 1>&2; builtin echo "$@"|grep .||true); }
|
|
warn() { (( LOG_LEVEL > -1 )) && (exec 1>&2; builtin echo "$@"|grep .||true); }
|
|
error() { (( LOG_LEVEL > -2 )) && (exec 1>&2; builtin echo "$@"|grep .||true); }
|
|
fatal() { (exec 1>&2; builtin echo "$@"|grep .||true); exit 4; }
|
|
log() { info "$@"; }
|
|
|
|
unbuf() { SCRIPT="$1"; shift ; stdbuf -oL -eL bash -c "$SCRIPT" - "$@" ; }
|
|
|
|
start() {
|
|
if ! options="$(getopt vq1 -- "$@")";then
|
|
fatal "$HELP"
|
|
fi
|
|
|
|
eval set "$options"
|
|
|
|
while [ $# -gt 0 ];do
|
|
case "$1" in
|
|
-1) ONCE=1 ;;
|
|
-h) info "$HELP"; exit 0 ;;
|
|
-v) LOG_LEVEL=$(( LOG_LEVEL + 1 )) ;;
|
|
-q) LOG_LEVEL=$(( LOG_LEVEL - 1 )) ;;
|
|
--) shift;break;; # we're _explicitly_ done with options
|
|
-*) error "invalid option $1"; fatal "$HELP" ;;
|
|
*) break;; # we're done with options.
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ "$VALIDATION" ];then
|
|
# fix up the validation regex to not search.
|
|
VALIDATION="${VALIDATION#^}"
|
|
VALIDATION="${VALIDATION%'$'}"
|
|
VALIDATION='^'"$VALIDATION"'$'
|
|
fi
|
|
|
|
info "Running with LOG_LEVEL=$LOG_LEVEL"
|
|
|
|
debug " VALIDATION: $VALIDATION"
|
|
debug " RATE_LIMIT: $RATE_LIMIT"
|
|
debug "POLL_INTERVAL: $POLL_INTERVAL"
|
|
debug " watch_cmd: $watch_cmd"
|
|
|
|
if [ "$(echo "$POLL_INTERVAL < $RATE_LIMIT"|bc)" = 1 ];then
|
|
warn "POLL_INTERVAL cannot be less than RATE_LIMIT."
|
|
info "increasing POLL_INTERVAL to $RATE_LIMIT."
|
|
POLL_INTERVAL=$RATE_LIMIT
|
|
fi
|
|
|
|
while true;do
|
|
_poll || exit $?
|
|
(( ONCE )) && exit 0
|
|
|
|
$watch_cmd | while sleep $RATE_LIMIT;do
|
|
# read with a maximum of the poll interval minus the previous rate limit.
|
|
# rounding _up_ to the nearest second. (bc floors when scale is unset.)
|
|
if read -r -t "$( echo "$POLL_INTERVAL - $RATE_LIMIT + .5 " | bc )";then
|
|
debug "received poll. removing other poll requests."
|
|
else
|
|
debug "reached the polling interval. forcing poll."
|
|
fi
|
|
N=0
|
|
while read -t 0;do read -r; N=$((N + 1));done
|
|
debug "removed $N extra events"
|
|
_poll
|
|
debug "sleeping for $RATE_LIMIT"
|
|
done
|
|
warn "tracker died. restarting after $RATE_LIMIT seconds."
|
|
sleep $RATE_LIMIT
|
|
done | unbuf "$_filter"
|
|
info "bye"
|
|
}
|
|
|
|
_poll() {
|
|
NEWVAL="$(poll | head -1)"
|
|
local skipped=0
|
|
echo "$NEWVAL" | while read -r LINE;do
|
|
(( skipped )) && info "$LINE"
|
|
skipped=1
|
|
done
|
|
|
|
local failed=0
|
|
if [ "$JQ_VALIDATION" ];then
|
|
echo "$NEWVAL" | jq -e "$JQ_VALIDATION" > /dev/null || failed=1
|
|
fi
|
|
if [ "$VALIDATION" ];then
|
|
echo "$NEWVAL" | grep -qE "$VALIDATION" || failed=1
|
|
fi
|
|
if (( failed ));then
|
|
warn "$NEWVAL failed validation"
|
|
else
|
|
echo "$NEWVAL"
|
|
fi
|
|
}
|
|
|
|
# MODULE OPTIONS
|
|
|
|
RATE_LIMIT=.1
|
|
rate_limit() {
|
|
RATE_LIMIT=$1
|
|
}
|
|
|
|
POLL_INTERVAL=60
|
|
poll_interval() {
|
|
POLL_INTERVAL=$1
|
|
}
|
|
|
|
_filter="uniq"
|
|
filter() {
|
|
_filter="$(printf " %q" "$@")"
|
|
}
|
|
|
|
# VALIDATORS
|
|
|
|
VALIDATION=.
|
|
output_integer() {
|
|
VALIDATION='^-?[0-9]+$'
|
|
}
|
|
|
|
output_boolean() {
|
|
VALIDATION='^true|false$'
|
|
}
|
|
|
|
output_string() {
|
|
VALIDATION="${1-.*}"
|
|
}
|
|
|
|
output_json() {
|
|
JQ_VALIDATION="${1-.}"
|
|
}
|
|
|
|
# EVENT HANDLERS
|
|
|
|
# each event source needs to emit a single _EMPTY_ line per event.
|
|
# poll will be called for each line.
|
|
on_volume() { on _on_volume; }
|
|
_on_volume() {
|
|
pactl subscribe | while read -r ;do
|
|
case "$REPLY" in
|
|
*change*) echo;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
on_sway() { on _on_sway; }
|
|
_on_sway() {
|
|
swaymsg -t subscribe -m '[ "workspace", "window" ]' |
|
|
jq --unbuffered -c . |
|
|
while read -r;do
|
|
echo
|
|
done
|
|
}
|
|
|
|
on() { watch_cmd="$1"; }
|