You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
12 KiB
Bash

4 years ago
#!/bin/sh
# Copyright (c) 2016, prpl Foundation
#
# 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
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Author: Nils Koenig <openwrt@newk.it>
. /lib/functions.sh
SCRIPT=$0
LOCKFILE=/tmp/wifi_schedule.lock
LOGFILE=/tmp/log/wifi_schedule.log
PACKAGE=wifi_schedule
GLOBAL=${PACKAGE}.@global[0]
# remember, if radio state was changed
radio_state_changed=0
# variable to remember if wifi should be enabled disabled (needed by check and apply function)
wifi_disabled=1
# variable to remember if scheduler is enabled
schedule_enabled=0
# variable to remember if stop should be forced
forcewifidown=0
_log()
{
if [ ${LOGGING} -eq 1 ]; then
local ts=$(date)
echo "$ts $@" >> ${LOGFILE}
fi
}
_exit()
{
local rc=$1
lock -u ${LOCKFILE}
exit ${rc}
}
_cron_restart()
{
/etc/init.d/cron restart > /dev/null
}
_add_cron_script()
{
(crontab -l ; echo "$1") | sort -u | crontab -
}
_rm_cron_script()
{
crontab -l | grep -v "$1" | sort -u | crontab -
}
_rm_and_add_cron_script()
{
(crontab -l | grep -v "$1"; [ -n "$2" ] && echo "$2") | sort -u | crontab -
}
_get_uci_value_raw()
{
local value
value=$(uci get $1 2> /dev/null)
local rc=$?
echo ${value}
return ${rc}
}
_get_uci_value()
{
local value
value=$(_get_uci_value_raw $1)
local rc=$?
if [ ${rc} -ne 0 ]; then
_log "Could not determine UCI value $1"
return 1
fi
echo ${value}
}
_format_dow_list()
{
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}
}
_shift_dow_list_by_one_day()
{
local dow=$1
local flist=""
local day
for day in $1
do
if [ ! -z ${flist} ]; then
flist="${flist},"
fi
case $day in
Mon*)
flist="${flist}Tue"
;;
Tue*)
flist="${flist}Wed"
;;
Wed*)
flist="${flist}Thu"
;;
Thu*)
flist="${flist}Fri"
;;
Fri*)
flist="${flist}Sat"
;;
Sat*)
flist="${flist}Sun"
;;
Sun*)
flist="${flist}Mon"
;;
esac
done
echo "$flist"
}
_enable_wifi_schedule()
{
local entry=$1
local enabled starttime stoptime dow
config_get enabled $entry enabled 0
[ "$enabled" -eq 0 ] && return 0
config_get starttime $entry starttime
[ -z "$starttime" ] && return 1
config_get stoptime $entry stoptime
[ -z "$stoptime" ] && return 1
config_get dow $entry daysofweek
[ -z "$dow" ] && return 1
local fdow=$(_format_dow_list "$dow")
local stopmode="stop"
if [ $forcewifidown -eq 1 ]; then
stopmode="forcestop"
fi
# If it shall stop on the same day at 00:00, 24:00 is the transition to the following day, if this is the case,
# the days of the weeks have to be incremented by one
if [ "${stoptime}" == "24:00" ]; then
fdow_stop=$(_shift_dow_list_by_one_day "$dow")
stoptime="0:00"
else
fdow_stop=${fdow}
fi
local stop_cron_entry="$(echo ${stoptime} | awk -F':' '{print $2, $1}') * * ${fdow_stop} ${SCRIPT} check" # ${entry}"
echo "${stop_cron_entry}"
if [[ $starttime != $stoptime ]]
then
local start_cron_entry="$(echo ${starttime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} check" # ${entry}"
echo "${start_cron_entry}"
fi
return 0
}
_get_wireless_interfaces()
{
local n=$(cat /proc/net/wireless | wc -l)
cat /proc/net/wireless | tail -n $(($n - 2))|awk -F':' '{print $1}'| sed 's/ //'
}
get_module_list()
{
local mod_list
local _if
for _if in $(_get_wireless_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)
done
echo $mod_list | tr ',' ' '
}
save_module_list_uci()
{
local list=$(get_module_list)
uci set ${GLOBAL}.modules="${list}"
uci commit ${PACKAGE}
}
_unload_modules()
{
local list=$(_get_uci_value ${GLOBAL}.modules)
local retries
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
_log "unload_modules ${list} (retries: ${retries})"
local i=0
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
do
i=$(($i+1))
local mod
local first=0
for mod in ${list}
do
if [ $first -eq 0 ]; then
list=""
first=1
fi
rmmod ${mod} > /dev/null 2>&1
if [ $? -ne 0 ]; then
list="$list $mod"
fi
done
done
}
_load_modules()
{
local list=$(_get_uci_value ${GLOBAL}.modules)
local retries
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
_log "load_modules ${list} (retries: ${retries})"
local i=0
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
do
i=$(($i+1))
local mod
local first=0
for mod in ${list}
do
if [ $first -eq 0 ]; then
list=""
first=1
fi
modprobe ${mod} > /dev/null 2>&1
rc=$?
if [ $rc -ne 255 ]; then
list="$list $mod"
fi
done
done
}
_create_cron_entries()
{
config_foreach _enable_wifi_schedule entry
}
check_cron_status()
{
config_load wifi_schedule
config_foreach handle_global global
if [ ${schedule_enabled} -eq 1 ]; then
_rm_and_add_cron_script "${SCRIPT}" "$(_create_cron_entries)"
else
_rm_cron_script "${SCRIPT}"
fi
_cron_restart
}
set_radio_state() {
local cfg="$1"
local wifi_disabled="$2"
local sched_disabled="$3"
config_get cur_state "$cfg" disabled 0
if [ "$wifi_disabled" = "1" ]; then
if [ "$wifi_disabled" != "$cur_state" ]; then
uci_set wireless "$cfg" disabled $wifi_disabled
radio_state_changed=1
fi
else
local value
config_get value $1 cfg_disabled '0' # default value if unset is 0 (on)
uci_set wireless $1 disabled $value
if [ "$sched_disabled" = "1" ]; then
[ "$value" != "$curstate" ] && radio_state_changed=1
else
[ "$value" = "0" ] && [ "$value" != "$curstate" ] && radio_state_changed=1
fi
fi
}
disable_wifi()
{
config_load wireless
_rm_cron_script "${SCRIPT} recheck"
_cron_restart
config_foreach set_radio_state wifi-device 1
[ "$radio_state_changed" -eq 1 ] && touch /tmp/cs_omit_wireless
[ "$radio_state_changed" -eq 1 ] && {
uci_commit wireless
reload_config # use procd to check for config changes
}
local unload_modules
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
if [[ "${unload_modules}" == "1" ]]; then
_unload_modules
fi
}
soft_disable_wifi()
{
local _disable_wifi=1
local iwinfo=/usr/bin/iwinfo
if [ ! -e ${iwinfo} ]; then
_log "${iwinfo} not available, skipping"
return 1
fi
# check if no stations are associated
local _if
for _if in $(_get_wireless_interfaces)
do
[ "${_if:0:3}" == "ath" ] || continue
output=$(${iwinfo} ${_if} assoclist)
if [[ "$output" != "No station connected" ]]
then
_disable_wifi=0
local stations=$(echo ${output}| grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' | tr '\n' ' ')
_log "Station(s) ${stations}associated on ${_if}"
fi
done
if [ ${_disable_wifi} -eq 1 ]; then
_log "No stations associated, disable wifi."
disable_wifi
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"
_cron_restart
fi
}
enable_wifi()
{
config_load wireless
_rm_cron_script "${SCRIPT} recheck"
_cron_restart
. /lib/delos-functions.sh
dvl_check_and_set_wifi_cfg_disabled
#reload config . maybe someting has been changed in the function above
config_load wireless
config_foreach set_radio_state wifi-device 0
local unload_modules
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
if [[ "${unload_modules}" == "1" ]]; then
_load_modules
fi
[ "$radio_state_changed" -eq 1 ] && touch /tmp/cs_omit_wireless
[ "$radio_state_changed" -eq 1 ] && {
uci_commit wireless
reload_config # use procd to check for config changes
}
}
disable_sched()
{
config_load wireless
config_foreach set_radio_state wifi-device 0 1
[ "$radio_state_changed" -eq 1 ] && {
uci_commit wireless
reload_config # use procd to check for config changes
}
}
handle_global() {
local cfg="$1"
config_get_bool schedule_enabled "$cfg" enabled 0
config_get_bool forcewifidown "$cfg" forcewifidown 0
}
handle_entry() {
local cfg="$1"
local entry_enabled=
local starttime=
local stoptime=
local dow=
local curTime=$(date "+%k%M")
local weekday=$(date "+%A")
config_get_bool entry_enabled "$cfg" enabled 0
config_get dow "$cfg" daysofweek
config_get starttime "$cfg" starttime
config_get stoptime "$cfg" stoptime
# Check if current entry is enabled
[ "$entry_enabled" -eq 0 ] && return 0
# Check if current entry is relevant for today
echo "$dow" | grep "$weekday" > /dev/null || return 0
#remove :, so that we can make easy integer comparison
starttime=${starttime//:}
stoptime=${stoptime//:}
[ $curTime -ge $starttime ] && [ $curTime -lt $stoptime ] && wifi_disabled=0
}
# Check if the radio should be turned on/off accordingly to the wifi scheduler settings, returns "off" or "on"
check_and_apply_wifischeduler_state() {
config_load wifi_schedule
config_foreach handle_global global
if [ "$schedule_enabled" -eq 0 ]; then
disable_sched
return 0
fi
config_foreach handle_entry entry
if [ "$wifi_disabled" -eq 0 ]; then
enable_wifi
else
if [ "$forcewifidown" -eq 1 ]; then
disable_wifi
else
soft_disable_wifi
fi
fi
}
usage()
{
echo ""
echo "$0 cron|start|stop|forcestop|recheck|getmodules|savemodules|help"
echo ""
echo " UCI Config File: /etc/config/${PACKAGE}"
echo ""
echo " cron: Create cronjob entries."
echo " start: Start wifi."
echo " stop: Stop wifi gracefully, i.e. check if there are stations associated and if so keep retrying."
echo " forcestop: Stop wifi immediately."
echo " recheck: Recheck if wifi can be disabled now."
echo " getmodules: Returns a list of modules used by the wireless driver(s)"
echo " savemodules: Saves a list of automatic determined modules to UCI"
echo " check: Check if should be turned on or not"
echo " help: This description."
echo ""
}
###############################################################################
# MAIN
###############################################################################
LOGGING=$(_get_uci_value ${GLOBAL}.logging) || LOGGING=0
_log ${SCRIPT} $1 $2
lock ${LOCKFILE}
config_load wireless
case "$1" in
cron) check_cron_status ;;
start) enable_wifi ;;
forcestop) disable_wifi ;;
stop) soft_disable_wifi ;;
recheck) soft_disable_wifi ;;
getmodules) get_module_list ;;
savemodules) save_module_list_uci ;;
check) check_and_apply_wifischeduler_state;;
help|--help|-h|*) usage ;;
esac
_exit 0