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
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
|