Files
op-packages/safeshield/files/usr/libexec/safeshield-refreshd
T

259 lines
5.9 KiB
Bash
Executable File

#!/bin/sh
# shellcheck shell=ash
# Variables below are populated by sourced helpers and ss_load_config()
# shellcheck disable=SC2154
LC_ALL=C
IPKG_INSTROOT="${IPKG_INSTROOT:-}"
export IPKG_INSTROOT
# shellcheck disable=SC1091
. /lib/functions.sh
# shellcheck disable=SC1091
. /usr/lib/safeshield/core.sh
readonly SS_REFRESHD_LOCK='/var/lock/safeshield-refreshd.lock'
readonly SS_REFRESHD_LOCK_FD=308
ss_should_terminate=0
ss_sleep_pid=''
refreshd_lock() {
mkdir -p "${SS_REFRESHD_LOCK%/*}" || return 1
eval "exec ${SS_REFRESHD_LOCK_FD}>\"${SS_REFRESHD_LOCK}\"" || return 1
flock -n "${SS_REFRESHD_LOCK_FD}" || return 1
}
refreshd_unlock() {
flock -u "${SS_REFRESHD_LOCK_FD}" 2>/dev/null || true
eval "exec ${SS_REFRESHD_LOCK_FD}>&-" 2>/dev/null || true
}
handle_term() {
ss_should_terminate=1
if [ -n "$ss_sleep_pid" ]; then
kill "$ss_sleep_pid" 2>/dev/null || true
fi
}
sleep_seconds() {
local seconds="$1"
local rc=0
[ "$ss_should_terminate" -eq 1 ] && return 1
[ "$seconds" -gt 0 ] 2>/dev/null || return 0
sleep "$seconds" &
ss_sleep_pid=$!
wait "$ss_sleep_pid" 2>/dev/null || rc=$?
ss_sleep_pid=''
[ "$ss_should_terminate" -eq 1 ] && return 1
[ "$rc" -eq 0 ]
}
wait_for_wan_if_needed() {
local require_wan
require_wan="$(ss_config_get config require_wan 1)"
case "$require_wan" in
1)
ubus -t 30 wait_for network.interface.wan 2>/dev/null || true
;;
esac
}
status_epoch() {
local key="$1"
local value
value="$(json get "$key" 2>/dev/null)"
is_valid_integer "$value" || value=0
printf '%s\n' "$value"
}
schedule_base_epoch() {
local now="$1"
local last_success last_attempt base=0
last_success="$(status_epoch last_success)"
last_attempt="$(status_epoch last_attempt)"
if [ "$last_success" -gt 0 ] 2>/dev/null && [ "$last_success" -le "$now" ] 2>/dev/null; then
base="$last_success"
fi
# A failed or currently-running refresh must also move the retry window
# forward so refreshd cannot enter an immediate retry loop. A successful
# refresh is naturally newer than its attempt timestamp and wins here.
if [ "$last_attempt" -gt "$base" ] 2>/dev/null && [ "$last_attempt" -le "$now" ] 2>/dev/null; then
base="$last_attempt"
fi
printf '%s\n' "$base"
}
has_recent_success() {
local interval="$1"
local now last_success age
now="$(date +%s)"
last_success="$(status_epoch last_success)"
[ "$last_success" -gt 0 ] 2>/dev/null || return 1
[ "$last_success" -le "$now" ] 2>/dev/null || return 1
age=$((now - last_success))
[ "$age" -lt "$interval" ] 2>/dev/null
}
set_next_refresh_epoch() {
local epoch="$1"
is_valid_integer "$epoch" || epoch=0
ss_status_set next_refresh_at "$epoch"
}
clear_next_refresh_at() {
ss_status_set next_refresh_at "0"
}
wait_until_refresh_due() {
local interval="$1"
local fallback_epoch="$2"
local now base due remaining
while :; do
[ "$ss_should_terminate" -eq 1 ] && return 1
now="$(date +%s)"
base="$(schedule_base_epoch "$now")"
if [ "$base" -le 0 ] 2>/dev/null; then
base="$fallback_epoch"
fi
due=$((base + interval))
remaining=$((due - now))
if [ "$remaining" -le 0 ] 2>/dev/null; then
clear_next_refresh_at
return 0
fi
set_next_refresh_epoch "$due"
sleep_seconds "$remaining" || return 1
# A manual refresh may have completed while refreshd was sleeping.
# Recalculate from the latest last_success/last_attempt before running.
done
}
main() {
local interval
local boot_delay
local refresh_on_boot
local schedule_anchor
refreshd_lock || exit 0
ss_load_config || exit 1
if [ ! -s "${RUNNING_STATUS_FILE}" ]; then
ss_status_reset || true
fi
ss_sync_blocklist_status
ss_status_set status "idle"
ss_status_set stage "starting"
interval="$(ss_config_get config refresh_interval_s 28800)"
boot_delay="$(ss_config_get config boot_start_delay_s 30)"
refresh_on_boot="$(ss_config_get config refresh_on_boot 1)"
is_valid_integer "$interval" || interval=28800
[ "$interval" -gt 0 ] 2>/dev/null || interval=28800
is_valid_integer "$boot_delay" || boot_delay=30
wait_for_wan_if_needed
ss_status_set stage "boot_delay"
set_next_refresh_epoch "$(($(date +%s) + boot_delay))"
sleep_seconds "$boot_delay" || exit 0
if [ "$refresh_on_boot" = "1" ] && [ "${ss_enabled}" = "1" ]; then
if has_recent_success "$interval"; then
log_info "Skipping boot refresh because a recent refresh already succeeded"
ss_status_set status "idle"
ss_status_set stage "boot_refresh_skipped"
else
ss_status_set status "running"
ss_status_set stage "boot_refresh"
clear_next_refresh_at
safeshield_force_download
fi
fi
schedule_anchor="$(date +%s)"
while :; do
ss_load_config || {
schedule_anchor="$(date +%s)"
sleep_seconds 5 || break
continue
}
interval="$(ss_config_get config refresh_interval_s 28800)"
is_valid_integer "$interval" || interval=28800
[ "$interval" -gt 0 ] 2>/dev/null || interval=28800
if [ "${ss_enabled}" != "1" ]; then
ss_status_set status "disabled"
ss_status_set stage "disabled"
clear_next_refresh_at
sleep_seconds "$interval" || break
schedule_anchor="$(date +%s)"
continue
fi
ss_status_set status "idle"
ss_status_set stage "scheduled_wait"
wait_until_refresh_due "$interval" "$schedule_anchor" || break
ss_load_config || {
schedule_anchor="$(date +%s)"
continue
}
if [ "${ss_enabled}" != "1" ]; then
ss_status_set status "disabled"
ss_status_set stage "disabled"
clear_next_refresh_at
schedule_anchor="$(date +%s)"
continue
fi
wait_for_wan_if_needed || true
ss_status_set status "running"
ss_status_set stage "scheduled_refresh"
clear_next_refresh_at
safeshield_force_download
schedule_anchor="$(date +%s)"
done
ss_load_config >/dev/null 2>&1 || true
if [ "${ss_enabled:-0}" = "1" ]; then
ss_status_set status "idle"
ss_status_set stage "stopped"
else
ss_status_set status "disabled"
ss_status_set stage "disabled"
fi
clear_next_refresh_at
}
trap "handle_term" TERM INT
trap "refreshd_unlock; clear_next_refresh_at" EXIT
main "$@"