Files
op-packages/librespeed-common/files/librespeed.init
T

149 lines
4.3 KiB
Bash

#!/bin/sh /etc/rc.common
# Keeps the scheduled-measurement cron entry in step with UCI. There is no
# daemon here: measurements are one-shot runs of librespeed-run, and the rpcd
# interface needs no service of its own.
START=95
USE_PROCD=1
CRONTAB=/etc/crontabs/root
RUN=/usr/libexec/librespeed-run
AGG=/usr/libexec/librespeed-aggregate
# Builds the cron time fields from UCI. For sub-daily intervals `hours`
# restricts when measurements may run; for a daily one it is the window a
# random time is drawn from, drawn here at sync time. That keeps the herd
# apart -- every router lands on its own minute -- without any process
# sleeping through the night, at the cost of re-drawing on every reload.
# Every comma-separated part must be a number or an ascending range within
# [0, max]; the caller already reduced the alphabet to digits, commas and
# dashes.
valid_field() {
local val="$1" max="$2" part a b oifs="$IFS"
IFS=','
for part in $val; do
IFS="$oifs"
a=${part%-*}; b=${part#*-}
# Each half on its own: concatenated, '5-' and '-5' hide their empty
# side and the arithmetic below would print a shell error where the
# caller promises a silent fallback.
case "$a" in ''|*[!0-9]*) return 1 ;; esac
case "$b" in ''|*[!0-9]*) return 1 ;; esac
[ "$a" -le "$max" ] && [ "$b" -le "$max" ] && [ "$a" -le "$b" ] || return 1
done
IFS="$oifs"
return 0
}
cron_expr() {
local interval="$1" days="$2" hours="$3"
local start end m h v
case "$interval" in
*m)
v=${interval%m}
case "$v" in ''|0|*[!0-9]*) return 1 ;; esac
[ "$v" -le 59 ] || return 1
echo "*/$v ${hours:-*} * * $days"
;;
*h)
v=${interval%h}
case "$v" in ''|0|*[!0-9]*) return 1 ;; esac
[ "$v" -le 23 ] || return 1
if [ -n "$hours" ]; then
echo "0 $hours/$v * * $days"
else
echo "0 */$v * * $days"
fi
;;
1d|24h)
start=${hours%-*}; end=${hours#*-}
[ -n "$hours" ] || { start=0; end=23; }
# A window wrapping midnight (22-4) is not supported; treat it as the
# single starting hour rather than guessing.
[ "$end" -ge "$start" ] 2>/dev/null || end=$start
# Time xor pid as the seed: seconds-granular srand() alone would give
# two syncs in the same second the same draw.
set -- $(awk -v a="$start" -v b="$end" -v s="$(( $(date +%s) ^ $$ ))" 'BEGIN {
srand(s)
printf "%d %d", int(rand() * 60), a + int(rand() * (b - a + 1))
}')
echo "$1 $2 * * $days"
;;
*)
return 1
;;
esac
}
restart_cron() {
# Unconditional: on a stock system the crontab dir starts empty, so crond
# is not running at all -- restart starts a stopped procd service, and a
# guard on "running" would mean the schedule never fires until reboot.
/etc/init.d/cron restart >/dev/null 2>&1
return 0
}
sync_cron() {
local enabled interval days hours expr hist_enabled archive_path
config_load librespeed
config_get_bool enabled schedule enabled 0
config_get interval schedule interval 1d
config_get days schedule days '*'
config_get hours schedule hours ''
config_get_bool hist_enabled history enabled 1
config_get archive_path history archive_path ''
# These end up verbatim in root's crontab, so anything unexpected becomes
# the harmless default rather than a cron field it was not meant to be.
# Values are checked as well as characters: '99' is made of digits, but
# as an hour it would turn a daily schedule into an hourly one.
case "$days" in *[!0-9,-]*|'') days='*' ;; esac
if [ "$days" != '*' ] && ! valid_field "$days" 6; then
days='*'
fi
case "$hours" in *[!0-9-]*) hours='' ;; esac
if [ -n "$hours" ] && ! valid_field "$hours" 23; then
hours=''
fi
mkdir -p "${CRONTAB%/*}"
touch "$CRONTAB"
sed -i "\\#$RUN#d;\\#$AGG#d" "$CRONTAB"
# The day completes at midnight; five past, it is reduced to one archive
# line. A missed run costs nothing to catch up: raw lives in RAM, so after
# a power cut there is nothing left to aggregate anyway.
if [ "$hist_enabled" = 1 ] && [ -n "$archive_path" ]; then
echo "5 0 * * * $AGG" >> "$CRONTAB"
fi
if [ "$enabled" = 1 ]; then
if expr=$(cron_expr "$interval" "$days" "$hours"); then
echo "$expr $RUN" >> "$CRONTAB"
else
logger -t librespeed "unsupported schedule interval '$interval'"
fi
fi
restart_cron
}
start_service() {
sync_cron
}
stop_service() {
sed -i "\\#$RUN#d;\\#$AGG#d" "$CRONTAB"
restart_cron
}
service_triggers() {
procd_add_reload_trigger "librespeed"
}
reload_service() {
sync_cron
}