This commit is contained in:
rindeal 2024-04-18 20:36:15 +03:00 committed by GitHub
commit 34944b8b9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 246 additions and 242 deletions

View File

@ -1,11 +1,12 @@
#!/bin/sh
# Copyright (c) 2016, prpl Foundation
# Copyright ANNO DOMINI 2024 Jan Chren (rindeal) <dev.rindeal(a)gmail.com>
#
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without
# fee is hereby granted, provided that the above copyright notice and this permission notice appear
# in all copies.
#
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
# FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
@ -16,187 +17,212 @@
set -o pipefail
SCRIPT=$0
SCRIPT="$0"
LOCKFILE=/tmp/wifi_schedule.lock
LOGFILE=/tmp/log/wifi_schedule.log
LOGGING=0 #default is off
PACKAGE=wifi_schedule
GLOBAL=${PACKAGE}.@global[0]
_arith_is() { [ "$1" -ne "0" ] ;}
_join_by_char()
{
local IFS="$1"
shift
printf "%s" "$*"
}
_log()
{
if [ ${LOGGING} -eq 1 ]; then
local ts=$(date)
echo "$ts $@" >> ${LOGFILE}
fi
[ "${LOGGING}" -eq 1 ] || return
local ts=$(date)
echo "$ts $@" >> "${LOGFILE}"
}
_exit()
{
local rc=$1
lock -u ${LOCKFILE}
lock -u "${LOCKFILE}"
exit ${rc}
}
_cron_restart()
{
/etc/init.d/cron restart > /dev/null
}
_add_cron_script()
{
(crontab -l ; echo "$1") | sort | uniq | crontab -
_cron_restart
}
_rm_cron_script()
{
crontab -l | grep -v "$1" | sort | uniq | crontab -
_cron_restart
}
_get_uci_value_raw()
{
local value
value=$(uci get $1 2> /dev/null)
local rc=$?
echo ${value}
return ${rc}
}
_get_uci_value_raw() { uci get "$1" 2> /dev/null ;}
_get_uci_value()
{
local value
value=$(_get_uci_value_raw $1)
_get_uci_value_raw "$1"
local rc=$?
if [ ${rc} -ne 0 ]; then
_log "Could not determine UCI value $1"
return 1
_log "Could not determine UCI value '$1'"
fi
echo ${value}
return ${rc}
}
_format_dow_list()
_cfg_global_is_enabled()
{
local dow=$1
local flist=""
local day
for day in ${dow}
do
if [ ! -z ${flist} ]; then
flist="${flist},"
fi
flist="${flist}${day:0:3}"
done
echo ${flist}
local enabled=$(_get_uci_value "${GLOBAL}.enabled")
test "${enabled}" -eq 1
}
_cfg_global_is_unload_modules_enabled()
{
local unload_modules=$(_get_uci_value_raw "${GLOBAL}.unload_modules") || return 1
test "${unload_modules}" -eq 1
}
_enable_wifi_schedule()
_cfg_list_entries()
{
uci show "${PACKAGE}" 2> /dev/null | awk -F= '$2 == "entry"' | cut -s -d. -f2 | cut -d= -f1
}
_cfg_entry_is_enabled()
{
local enabled=$(_get_uci_value "${PACKAGE}.${entry}.enabled")
test "${enabled}" -eq 1
}
_cfg_entry_is_now_within_timewindow()
{
local entry=$1
local starttime
local stoptime
starttime=$(_get_uci_value ${PACKAGE}.${entry}.starttime) || _exit 1
stoptime=$(_get_uci_value ${PACKAGE}.${entry}.stoptime) || _exit 1
local starttime=$( _get_uci_value "${PACKAGE}.${entry}.starttime" ) || return 1
local stoptime=$( _get_uci_value "${PACKAGE}.${entry}.stoptime" ) || return 1
local daysofweek=$(_get_uci_value "${PACKAGE}.${entry}.daysofweek") || return 1
# check if day of week matches today
echo "$daysofweek" | grep -q "$(date +%A)" || return 1
local nowts=$( date -u +%s -d "$(date "+%H:%M")")
local startts=$(date -u +%s -d "$starttime")
local stopts=$( date -u +%s -d "$stoptime")
# add a day if stopts goes past midnight
stopts=$(( stopts < startts ? stopts + 86400 : stopts ))
_arith_is $(( nowts >= startts && nowts < stopts ))
}
_cfg_can_wifi_run_now()
{
local entry
for entry in $(_cfg_list_entries)
do
test -n "${entry}" || continue
_cfg_entry_is_enabled "${entry}" || continue
_cfg_entry_is_now_within_timewindow "$entry" && return 0
done
return 1
}
_cron_restart() { service cron restart > /dev/null ;}
_crontab_add_entry_raw() { (crontab -l ; printf "%s\n" "$1") | crontab - ;}
_crontab_rm_script_entries_by_arg()
{
# this loop will create regexp that looks like this:
#
# ^\b${SCRIPT}\b\s+\b${@}\b
#
local regex="(:?^|[[:space:]])${SCRIPT}"
local arg
for arg in "$@"
do
regex="${regex}[[:space:]]{1,}${arg}"
done
regex="${regex}(:?$|[[:space:]])"
crontab -l | awk -v cmd_field_n=6 -v regex="${regex}" '
{
is_blank_or_comment = $0 ~ /^[[:space:]]*(:?#.*)?$/
is_env_var = $0 ~ /^[[:space:]]*[a-zA-Z_]{1,}[a-zA-Z0-9_]{0,}[[:space:]]*=.*$/
# find index of cmdline start
match($0, "[[:space:]]*([^[:space:]]+[[:space:]]+){" cmd_field_n - 1 "}")
cmdline = substr($0, RLENGTH + 1)
if ( is_blank_or_comment || is_env_var || cmdline !~ regex )
{
print
next
}
}' | crontab -
}
_crontab_format_dow_field()
{
_join_by_char "," $(printf "%.3s\n" $@)
}
_crontab_add_from_cfg_entry()
{
local entry=$1
local starttime=$( _get_uci_value "${PACKAGE}.${entry}.starttime" ) || return 1
local stoptime=$( _get_uci_value "${PACKAGE}.${entry}.stoptime" ) || return 1
local daysofweek=$( _get_uci_value "${PACKAGE}.${entry}.daysofweek") || return 1
local forcewifidown=$(_get_uci_value "${PACKAGE}.${entry}.forcewifidown")
local starthh=$(expr substr "$starttime" 1 2) startmm=$(expr substr "$starttime" 4 2)
local stophh=$( expr substr "$stoptime" 1 2) stopmm=$( expr substr "$stoptime" 4 2)
local dow
dow=$(_get_uci_value_raw ${PACKAGE}.${entry}.daysofweek) || _exit 1
local fdow=$(_format_dow_list "$dow")
local forcewifidown
forcewifidown=$(_get_uci_value ${PACKAGE}.${entry}.forcewifidown)
local stopmode="stop"
if [ $forcewifidown -eq 1 ]; then
if [ "$forcewifidown" -eq 1 ]; then
stopmode="forcestop"
fi
local fdow=$(_crontab_format_dow_field $daysofweek)
local stop_cron_entry="$(echo ${stoptime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} ${stopmode}" # ${entry}"
_add_cron_script "${stop_cron_entry}"
if [[ $starttime != $stoptime ]]
then
local start_cron_entry="$(echo ${starttime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} start" # ${entry}"
_add_cron_script "${start_cron_entry}"
if [ "$starttime" != "$stoptime" ]
then
_crontab_add_entry_raw "$startmm $starthh * * ${fdow} ${SCRIPT} start ${entry}"
fi
_crontab_add_entry_raw "$stopmm $stophh * * ${fdow} ${SCRIPT} ${stopmode} ${entry}"
return 0
}
_is_earlier()
_crontab_reset_from_cfg()
{
local hhmm=$1
local ret=1
if [[ $(date +%H) -lt ${hhmm:0:2} ]]
then
ret=0
fi
if [[ $(date +%H) -eq ${hhmm:0:2} && $(date +%M) -lt ${hhmm:3:4} ]]
then
ret=0
fi
echo $ret
_crontab_rm_script_entries_by_arg
_cfg_global_is_enabled || return
local entry
for entry in $(_cfg_list_entries)
do
test -n "${entry}" || continue
_cfg_entry_is_enabled "${entry}" || continue
_crontab_add_from_cfg_entry "${entry}"
done
}
# returns 0 if now() is in $entry
_check_startup_timewindow()
{
local entry=$1
local starttime
local stoptime
local dow
starttime=$(_get_uci_value ${PACKAGE}.${entry}.starttime) || _exit 1
stoptime=$(_get_uci_value ${PACKAGE}.${entry}.stoptime) || _exit 1
dow=$(_get_uci_value_raw ${PACKAGE}.${entry}.daysofweek) || _exit 1
echo $dow | grep $(date +%A) > /dev/null 2>&1
rc=$?
if [[ $rc -eq 0 && $(date +%H) -ge ${starttime:0:2} && $(date +%M) -ge ${starttime:3:4} && $(_is_earlier $stoptime) -eq 0 ]]
then
echo 0
else
echo 1
fi
}
_get_wireless_interfaces()
{
iwinfo | grep ESSID | cut -f 1 -s -d" "
}
get_module_list()
{
local mod_list
local _if
for _if in $(_get_wireless_interfaces)
for _if in $(_wifi_get_interfaces)
do
local mod=$(basename $(readlink -f /sys/class/net/${_if}/device/driver))
local mod_dep=$(modinfo ${mod} | awk '{if ($1 ~ /depends/) print $2}')
mod_list=$(echo -e "${mod_list}\n${mod},${mod_dep}" | sort | uniq)
local mod=$(basename "$(readlink -f /sys/class/net/${_if}/device/driver)")
local mod_dep=$(modinfo "${mod}" | awk '$1 ~ /^depends:/ { print $2 }')
mod_list=$(echo -e "${mod_list}\n${mod},${mod_dep}" | sort -u)
done
echo $mod_list | tr ',' ' '
echo "$mod_list" | tr ',' ' '
}
save_module_list_uci()
{
local list=$(get_module_list)
uci set ${GLOBAL}.modules="${list}"
uci commit ${PACKAGE}
uci commit "${PACKAGE}"
}
_unload_modules()
{
local list=$(_get_uci_value ${GLOBAL}.modules)
local retries
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
local list=$(_get_uci_value "${GLOBAL}.modules")
local retries=$(_get_uci_value "${GLOBAL}.modules_retries") || return 1
_log "unload_modules ${list} (retries: ${retries})"
local i=0
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
do
i=$(($i+1))
while [ $i -lt "$retries" -a -n "$list" ]
do
: $(( ++i ))
local mod
local first=0
for mod in ${list}
@ -205,7 +231,7 @@ _unload_modules()
list=""
first=1
fi
rmmod ${mod} > /dev/null 2>&1
rmmod "${mod}" > /dev/null 2>&1
if [ $? -ne 0 ]; then
list="$list $mod"
fi
@ -213,17 +239,15 @@ _unload_modules()
done
}
_load_modules()
{
local list=$(_get_uci_value ${GLOBAL}.modules)
local retries
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
local list=$( _get_uci_value "${GLOBAL}.modules") || return 1
local retries=$(_get_uci_value "${GLOBAL}.modules_retries") || return 1
_log "load_modules ${list} (retries: ${retries})"
local i=0
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
do
i=$(($i+1))
while [ $i -lt "${retries}" -a -n "${list}" ]
do
: $(( ++i ))
local mod
local first=0
for mod in ${list}
@ -232,8 +256,8 @@ _load_modules()
list=""
first=1
fi
modprobe ${mod} > /dev/null 2>&1
rc=$?
modprobe "${mod}" > /dev/null 2>&1
rc=$?
if [ $rc -ne 255 ]; then
list="$list $mod"
fi
@ -241,143 +265,122 @@ _load_modules()
done
}
_create_cron_entries()
_uci_wireless_radio_set_disabled_to()
{
local entries=$(uci show ${PACKAGE} 2> /dev/null | awk -F'.' '{print $2}' | grep -v '=' | grep -v '@global\[0\]' | uniq | sort)
local _entry
for entry in ${entries}
do
local status
status=$(_get_uci_value ${PACKAGE}.${entry}.enabled) || _exit 1
if [ ${status} -eq 1 ]
then
_enable_wifi_schedule ${entry}
fi
done
}
_should_wifi_enabled()
{
local enable_wifi=0
local entries=$(uci show ${PACKAGE} 2> /dev/null | awk -F'.' '{print $2}' | grep -v '=' | grep -v '@global\[0\]' | uniq | sort)
local _entry
for _entry in ${entries}
local status=$1
_arith_is $(( status == 0 || status == 1 )) || return 1
for radio in $(uci show wireless | awk -F= '$2 == "wifi-device" {print $1}' | awk -F. '{print $2}')
do
local status
status=$(_get_uci_value ${PACKAGE}.${_entry}.enabled) || _exit 1
if [ ${status} -eq 1 ]
then
enable_wifi=$(_check_startup_timewindow $_entry)
fi
uci set "wireless.${radio}.disabled=${status}"
done
echo ${enable_wifi}
uci commit
/sbin/wifi
}
startup()
_uci_enable_all_radio_devices() { _uci_wireless_radio_set_disabled_to 0 ;}
_uci_disable_all_radio_devices() { _uci_wireless_radio_set_disabled_to 1 ;}
_wifi_get_interfaces()
{
_log "startup"
local global_enabled=$(_get_uci_value ${GLOBAL}.enabled) || _exit 1
if [ ${global_enabled} -eq 1 ]; then
local _enable_wifi=$(_should_wifi_enabled)
if [ ${_enable_wifi} -eq 0 ]; then
_log "enable wifi"
enable_wifi
else
_log "disable wifi"
disable_wifi
fi
fi
iwinfo | grep ESSID | cut -f 1 -d" " -s
}
check_cron_status()
wifi_disable()
{
local global_enabled
global_enabled=$(_get_uci_value ${GLOBAL}.enabled) || _exit 1
_rm_cron_script "${SCRIPT}"
if [ ${global_enabled} -eq 1 ]; then
_create_cron_entries
fi
}
disable_wifi()
{
_rm_cron_script "${SCRIPT} recheck"
_set_status_wifi_uci 1
local unload_modules
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
if [[ "${unload_modules}" == "1" ]]; then
_crontab_rm_script_entries_by_arg "recheck"
_cron_restart
_uci_disable_all_radio_devices
if _cfg_global_is_unload_modules_enabled
then
_unload_modules
fi
fi
}
soft_disable_wifi()
wifi_soft_disable()
{
local _disable_wifi=0 #0: disable wifi, 1: do not disable wifi
local iwinfo=/usr/bin/iwinfo
if [ ! -e ${iwinfo} ]; then
_log "${iwinfo} not available, skipping"
if ! command -v iwinfo 2>/dev/null ; then
_log "iwinfo not available, skipping"
return 1
fi
local ignore_stations=$(_get_uci_value_raw ${GLOBAL}.ignore_stations)
[ -n "${ignore_stations}" ] && _log "Ignoring station(s) ${ignore_stations}"
local has_assoc=false
local mac_filter='([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'
local ignore_stations=$(_get_uci_value_raw "${GLOBAL}.ignore_stations")
local ignore_stations_filter=$( _join_by_char "|" ${ignore_stations} )
# check if no stations are associated
local _if
for _if in $(_get_wireless_interfaces)
for _if in $(_wifi_get_interfaces)
do
local stations=$(${iwinfo} ${_if} assoclist | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
local stations ignored_stations
stations=$(iwinfo "${_if}" assoclist | grep -o -E "${mac_filter}")
if [ -n "${ignore_stations}" ]; then
stations=$(echo "${stations}" | grep -vwi -E "${ignore_stations// /|}")
local all_stations="$stations"
stations=$(printf "%s\n" ${stations} | grep -vwi -E "${ignore_stations_filter}")
ignored_stations="$( printf "%s\n" $all_stations $stations | sort | uniq -u )"
fi
if [ -n "${stations}" ]; then
_disable_wifi=1
_log "Station(s) $(echo ${stations}) associated on ${_if}"
test -n "${stations}" || continue
has_assoc=true
_log "Clients connected on '${_if}': $(_join_by_char ' ' ${stations})"
if test -n "${ignored_stations}"
then
_log "Clients ignored on '${_if}': $(_join_by_char ' ' ${ignored_stations})"
fi
done
local _wifi_enabled=$(_should_wifi_enabled)
if [[ ${_disable_wifi} -eq 0 && ${_wifi_enabled} -eq 1 ]]; then
_log "No stations associated, disable wifi."
disable_wifi
elif [[ ${_disable_wifi} -eq 0 && ${_wifi_enabled} -eq 0 ]]; then
_log "Do not disable wifi since there is an allow timeframe, skip rechecking."
_rm_cron_script "${SCRIPT} recheck"
_crontab_rm_script_entries_by_arg "recheck"
if [ "$has_assoc" = "false" ]
then
if _cfg_can_wifi_run_now
then
_log "Do not disable wifi since there is an allow timewindow, skip rechecking."
else
_log "No stations associated, disable wifi."
wifi_disable
fi
else
_log "Could not disable wifi due to associated stations, retrying..."
local recheck_interval=$(_get_uci_value ${GLOBAL}.recheck_interval)
_add_cron_script "*/${recheck_interval} * * * * ${SCRIPT} recheck"
local recheck_interval=$(_get_uci_value "${GLOBAL}.recheck_interval")
if [ -n "${recheck_interval}" -a "${recheck_interval}" -gt 0 ] ; then
_crontab_add_entry_raw "*/${recheck_interval} * * * * /bin/nice -n 19 ${SCRIPT} recheck"
fi
fi
_cron_restart
}
_set_status_wifi_uci()
wifi_enable()
{
local status=$1
local radios=$(uci show wireless | grep radio | awk -F'.' '{print $2}' | grep -v '[=|@]' | sort | uniq)
for radio in ${radios}
do
uci set wireless.${radio}.disabled=${status}
done
uci commit
}
enable_wifi()
{
_rm_cron_script "${SCRIPT} recheck"
local unload_modules
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
if [[ "${unload_modules}" == "1" ]]; then
_crontab_rm_script_entries_by_arg "recheck"
_cron_restart
if _cfg_global_is_unload_modules_enabled
then
_load_modules
fi
_set_status_wifi_uci 0
/sbin/wifi
_uci_enable_all_radio_devices
}
wifi_startup()
{
_cfg_global_is_enabled || return
if _cfg_can_wifi_run_now
then
_log "enable wifi"
wifi_enable
else
_log "disable wifi"
wifi_disable
fi
}
usage()
{
echo ""
echo "$0 cron|start|startup|stop|forcestop|recheck|getmodules|savemodules|help"
echo ""
echo " UCI Config File: /etc/config/${PACKAGE}"
@ -396,8 +399,8 @@ usage()
_cleanup()
{
lock -u ${LOCKFILE}
rm ${LOCKFILE}
lock -u "${LOCKFILE}"
rm "${LOCKFILE}"
}
###############################################################################
@ -405,20 +408,21 @@ _cleanup()
###############################################################################
trap _cleanup EXIT
LOGGING=$(_get_uci_value ${GLOBAL}.logging) || _exit 1
_log ${SCRIPT} $1 $2
lock ${LOCKFILE}
LOGGING=$(_get_uci_value "${GLOBAL}.logging") || _exit 1
_log "${SCRIPT}" "$@"
lock "${LOCKFILE}"
case "$1" in
cron)
check_cron_status
startup
cron)
_crontab_reset_from_cfg
_cron_restart
wifi_startup
;;
start) enable_wifi ;;
startup) startup ;;
forcestop) disable_wifi ;;
stop) soft_disable_wifi ;;
recheck) soft_disable_wifi ;;
start) wifi_enable ;;
startup) wifi_startup ;;
forcestop) wifi_disable ;;
stop) wifi_soft_disable ;;
recheck) wifi_soft_disable ;;
getmodules) get_module_list ;;
savemodules) save_module_list_uci ;;
help|--help|-h|*) usage ;;