#!/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 . /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