wifischedule: completely overhaul the script
authorJan Chren ~rindeal <dev.rindeal@gmail.com>
Thu, 4 Jul 2024 08:58:40 +0000 (08:58 +0000)
committerRosen Penev <rosenp@gmail.com>
Fri, 5 Jul 2024 20:43:57 +0000 (13:43 -0700)
Squashed using these commits:

 - wifischedule: use `service` instead of direct path
 - wifischedule: use `sort -u` instead of `uniq`
 - wifischedule: restart cron only at the end of batch instead of after every change
 - wifischedule: remove `[[` bash-isms
 - wifischedule: trim trailing ws
 - wifischedule: reduce `if` blocks
 - wifischedule: quote variables and remove some more bash-isms
 - wifischedule: simplify _get_uci_value`
 - wifischedule: don't exit whole script just because `uci get somekey` fails somewhere
 - wifischedule: revamp `_should_enable_wifi()`
 - wifischedule: revamp `_format_daysofweek_list()`
 - wifischedule: revamp `_enable_wifi_schedule()`
 - wifischedule: minor refactoring
 - wifischedule: mega revamp
 - wifischedule: fixes
 - wifischedule: touch-ups
 - wifischedule: use only `awk` in `_cfg_list_entries()` to filter `uci`
 - wifischedule: improve code docs
 - wifischedule: inline `_crontab_format_dow_field()`
 - wifischedule: refactor `_crontab_append_line()`
 - wifischedule: add `_uci_bool()` and refactor `_arith_bool()`
 - wifischedule: rename some functions
 - wifischedule: refactor using shellcheck
 - wifischedule: refactor `_wifi_get_interfaces()`
 - wifischedule: refactor `_wifi_get_devices()`
 - wifischedule: shellcheck fixes
 - wifischedule: use logger instead of a logfile
 - wifischedule: refactor global consts
 - wifischedule: introduce main() func
 - wifischedule: bump version

Signed-off-by: Jan Chren ~rindeal <dev.rindeal@gmail.com>
net/wifischedule/Makefile
net/wifischedule/net/usr/bin/wifi_schedule.sh

index 7dc28a9fb8fcdb3aacbe3e1507a2d675fc0fb542..6602d6aa586c6141778b5fad17a8f666c3745b85 100644 (file)
@@ -15,8 +15,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=wifischedule
-PKG_VERSION:=1.0.5
-PKG_RELEASE:=2
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=0
 PKG_LICENSE:=PRPL
 
 PKG_MAINTAINER:=Nils Koenig <openwrt@newk.it> 
index 9adadd212f3c9e561b3f1a337cbdb924da98c8aa..9f91bc66c8820de332bea2deff3d5e1e70f266f3 100755 (executable)
@@ -1,11 +1,13 @@
-#!/bin/sh
+#!/bin/ash
+# shellcheck shell=dash
 
 # 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
 
 set -o pipefail
 
-SCRIPT=$0
-LOCKFILE=/tmp/wifi_schedule.lock
-LOGFILE=/tmp/log/wifi_schedule.log
+
+SCRIPT="$0"
+PACKAGE="wifi_schedule"
+GLOBAL="${PACKAGE}.@global[0]"
+LOCKFILE="/tmp/${PACKAGE}.lock"
 LOGGING=0 #default is off
-PACKAGE=wifi_schedule
-GLOBAL=${PACKAGE}.@global[0]
 
+
+# Converts the result of arithmetic expansion to a normal command return code
+# Usage: if _arith_bool $(( 3*4 > 12 && foo <= bar )) ...
+_arith_bool() { [ "$1" -ne 0 ] ;}
+
+# Usage: if _uci_bool $(_uci_get_value "foo.bar") ...
+_uci_bool() { [ "$1" -eq 1 ] ;}
+
+# Usage: _join_by_char , foo bar baz
+# Prints: `foo.bar,baz`
+_join_by_char()
+{
+    local IFS
+    IFS="$1"
+    shift
+    printf "%s" "$*"
+}
+
+## Usage: _log [emerg, alert, crit, err, warning, notice, info, debug] "Message ..."
 _log()
 {
-    if [ ${LOGGING} -eq 1 ]; then
-        local ts=$(date)
-        echo "$ts $@" >> ${LOGFILE}
-    fi
+    local severity="$1"
+    shift
+    _uci_bool "${LOGGING}" || return
+    logger -t "${PACKAGE}" -p "user.${severity}" "$@"
 }
 
 _exit()
 {
-    local rc=$1
-    lock -u ${LOCKFILE}
-    exit ${rc}
+    lock -u "${LOCKFILE}"
+    exit "$1"
 }
 
-_cron_restart()
+_uci_get_value_raw() { uci get "$1" 2> /dev/null ;}
+
+_uci_get_value()
 {
-    /etc/init.d/cron restart > /dev/null
+    _uci_get_value_raw "$1"
+    local rc=$?
+    if [ "${rc}" -ne 0 ]; then
+        _log "notice" "Could not determine UCI value '$1'"
+    fi
+    return "${rc}"
 }
 
-_add_cron_script()
-{
-    (crontab -l ; echo "$1") | sort | uniq | crontab -
-    _cron_restart
+_cfg_global_is_enabled() {
+    local value
+    value="$(_uci_get_value "${GLOBAL}.enabled")"
+    _uci_bool "${value}"
 }
 
-_rm_cron_script()
+_cfg_global_is_unload_modules_enabled()
 {
-    crontab -l | grep -v "$1" |  sort | uniq | crontab -
-    _cron_restart
+    local unload_modules
+    unload_modules="$(_uci_get_value_raw "${GLOBAL}.unload_modules")" || return 1
+    _uci_bool "${unload_modules}"
 }
 
-_get_uci_value_raw()
+# Prints: `entry1_name$'\n'entry2_name$'\n'...`
+_cfg_list_entries()
 {
+    uci show "${PACKAGE}" | awk -F= '$2 == "entry" { n = split($1, a, "."); print a[n] }'
+}
+
+_cfg_entry_is_enabled() {
     local value
-    value=$(uci get $1 2> /dev/null)
-    local rc=$?
-    echo ${value}
-    return ${rc}
+    value="$(_uci_get_value "${PACKAGE}.${entry}.enabled")"
+    _uci_bool "${value}"
 }
 
-_get_uci_value()
+_cfg_entry_is_now_within_timewindow()
 {
-    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}
+    local entry="$1"
+    local starttime stoptime daysofweek
+    local nowdow nowhhmm nowts startts stopts
+    starttime=$( _uci_get_value "${PACKAGE}.${entry}.starttime" ) || return 1
+    stoptime=$(  _uci_get_value "${PACKAGE}.${entry}.stoptime"  ) || return 1
+    daysofweek=$(_uci_get_value "${PACKAGE}.${entry}.daysofweek") || return 1
+
+    # check if day of week matches today
+    nowdow="$(date +%A)"
+    echo "${daysofweek}" | grep -q "${nowdow}" || return 1
+
+    nowhhmm="$(date "+%H:%M")"
+    nowts=$(  date -u +%s -d "${nowhhmm}")
+    startts=$(date -u +%s -d "${starttime}")
+    stopts=$( date -u +%s -d "${stoptime}")
+    # add a day if stopts goes past midnight
+    stopts=$(( stopts < startts ? stopts + 86400 : stopts ))
+
+    _arith_bool $(( nowts >= startts && nowts < stopts ))
 }
 
-_format_dow_list()
+_cfg_can_wifi_run_now()
 {
-    local dow=$1
-    local flist=""
-    local day
-    for day in ${dow}
+    local entry
+    for entry in $(_cfg_list_entries)
     do
-        if [ ! -z ${flist} ]; then
-            flist="${flist},"
-        fi
-        flist="${flist}${day:0:3}"
+        test -n "${entry}" || continue
+        _cfg_entry_is_enabled "${entry}" || continue
+        _cfg_entry_is_now_within_timewindow "${entry}" && return 0
     done
-    echo ${flist}
+    return 1
 }
 
+_cron_restart() { service cron restart > /dev/null ;}
 
-_enable_wifi_schedule()
-{
-    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 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
-        stopmode="forcestop"
-    fi
+# shellcheck disable=SC2312
+_crontab_append_line() { (crontab -l ; printf "%s\n" "$(_join_by_char ' ' "$@")") | crontab - ;}
 
+## Usage: _crontab_rm_script_entries_by_arg          # this removes all script entries
+## Usage: _crontab_rm_script_entries_by_arg recheck  # this removes just entries with recheck argument
+_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:]]+${arg}"
+    done
+    regex="${regex}(:?$|[[:space:]])"
 
-    local stop_cron_entry="$(echo ${stoptime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} ${stopmode}" # ${entry}"
-    _add_cron_script "${stop_cron_entry}"
+    crontab -l | awk -v cmd_col_pos=6 -v regex="${regex}" '
+        {
+            is_blank_or_comment = $0 ~ /^[[:space:]]*(:?#.*)?$/
+            is_env_var          = $0 ~ /^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=.*$/
 
-    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}"
-    fi
+            # find index of the cmdline cell start
+            match($0, "[[:space:]]*([^[:space:]]+[[:space:]]+){" cmd_col_pos - 1 "}")
+            # get the cmdline cell
+            cmdline = substr($0, RLENGTH + 1)
 
-    return 0
+            if ( is_blank_or_comment || is_env_var || cmdline !~ regex )
+                print
+        }' | crontab -
 }
 
-_is_earlier()
+_crontab_add_from_cfg_entry()
 {
-    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} ]]
+    local entry="$1"
+    local starttime stoptime daysofweek forcewifidown 
+    starttime=$(    _uci_get_value "${PACKAGE}.${entry}.starttime" ) || return 1
+    stoptime=$(     _uci_get_value "${PACKAGE}.${entry}.stoptime"  ) || return 1
+    daysofweek=$(   _uci_get_value "${PACKAGE}.${entry}.daysofweek") || return 1
+    forcewifidown=$(_uci_get_value "${PACKAGE}.${entry}.forcewifidown")
+    
+    # parse `HH:MM` to `Xhh` and `Xmm` variables
+    local starthh stophh startmm stopmm
+    starthh=$(echo "${starttime}" | cut -c 1-2) startmm=$(echo "${starttime}" | cut -c 4-5) || true
+    stophh=$( echo "${stoptime}"  | cut -c 1-2) stopmm=$( echo "${stoptime}"  | cut -c 4-5) || true
+
+    local fdow
+    # shellcheck disable=SC2046,SC2086
+    fdow=$(_join_by_char "," $(printf "%.3s\n" ${daysofweek}))
+
+    if [ "${starttime}" != "${stoptime}" ]
     then
-        ret=0
+        _crontab_append_line "${startmm} ${starthh} * * ${fdow} ${SCRIPT} start ${entry}"
     fi
-    echo $ret
-}
 
-# 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
+    local stopmode="stop"
+    if _uci_bool "${forcewifidown}" ; then
+        stopmode="forcestop"
     fi
+
+    _crontab_append_line "${stopmm} ${stophh} * * ${fdow} ${SCRIPT} ${stopmode} ${entry}"
+
+    return 0
 }
 
-_get_wireless_interfaces()
+_crontab_reset_from_cfg()
 {
-    iwinfo | grep ESSID | cut -f 1 -s -d" "
+    _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
 }
 
+# region: kernel module unload feature
 
 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 mod_dep
+        # trunk-ignore(shellcheck/SC2312)
+        mod=$(basename "$(readlink -f "/sys/class/net/${_if}/device/driver")")
+        mod_dep=$(modinfo "${mod}" | awk '$1 ~ /^depends:/ { print $2 }')
+        mod_list=$(printf "%s\n%s,%s" "${mod_list}" "${mod}" "${mod_dep}" | sort -u)
     done
-    echo $mod_list | tr ',' ' '
+    # trunk-ignore(shellcheck/SC2250)
+    echo "$mod_list" | tr ',' ' '
 }
 
 save_module_list_uci()
 {
-    local list=$(get_module_list)
-    uci set ${GLOBAL}.modules="${list}"
-    uci commit ${PACKAGE}
+    local list
+    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 list retries
+    list=$(_uci_get_value "${GLOBAL}.modules")
+    retries=$(_uci_get_value "${GLOBAL}.modules_retries") || return 1
+    _log "info" "unload_modules ${list} (retries: ${retries})"
     local i=0
-    while [[ ${i} -lt ${retries}  &&  "${list}" != "" ]]
-    do  
-        i=$(($i+1))
+    # trunk-ignore(shellcheck/SC2250)
+    while _arith_bool $(( i < retries )) && test -n "$list"
+    do
+        : $(( i += 1 ))
         local mod
         local first=0
         for mod in ${list}
         do
-            if [ $first -eq 0 ]; then
+            if [ "${first}" -eq 0 ]; then
                 list=""
                 first=1
             fi
-            rmmod ${mod} > /dev/null 2>&1
-            if [ $? -ne 0 ]; then
+            
+            if ! rmmod "${mod}" >/dev/null 2>&1 ; then
+                # trunk-ignore(shellcheck/SC2250)
                 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 list retries
+    list=$(   _uci_get_value "${GLOBAL}.modules") || return 1
+    retries=$(_uci_get_value "${GLOBAL}.modules_retries") || return 1
+    _log "info" "load_modules ${list} (retries: ${retries})"
     local i=0
-    while [[ ${i} -lt ${retries}  &&  "${list}" != "" ]]
-    do  
-        i=$(($i+1))
+    # trunk-ignore(shellcheck/SC2250)
+    while _arith_bool $(( i < retries )) && test -n "$list"
+    do
+        : $(( i += 1 ))
         local mod
         local first=0
         for mod in ${list}
         do
-            if [ $first -eq 0 ]; then
+            if [ "${first}" -eq 0 ]; then
                 list=""
                 first=1
             fi
-            modprobe ${mod} > /dev/null 2>&1
-            rc=$? 
-            if [ $rc -ne 255 ]; then
+            modprobe "${mod}" > /dev/null 2>&1
+            rc=$?
+            if [ "${rc}" -ne 255 ]; then
+                # trunk-ignore(shellcheck/SC2250)
                 list="$list $mod"
             fi
         done
     done
 }
 
-_create_cron_entries()
-{
-    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
-}
+# endregion: kernel module unload feature
 
-_should_wifi_enabled() 
-{
+# Prints: `phy0-ap0$'\n'phy0-ap1$'\n'`
+_wifi_get_interfaces() { iwinfo | awk '/[^[:alnum:]]ESSID[^[:alnum:]]/ { print $1 }' ;}
 
-    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}
-    do
-        local status
-        status=$(_get_uci_value ${PACKAGE}.${_entry}.enabled) || _exit 1
-        if [ ${status} -eq 1 ]
-        then
-            enable_wifi=$(_check_startup_timewindow $_entry)
-        fi
-    done
-    echo ${enable_wifi}
-}
+# Prints: `radio0$'\n'radio1$'\n'`
+_wifi_get_devices() { uci show "wireless" | awk -F= '$2 == "wifi-device" { n = split($1, a, "."); print a[n] }' ;}
 
-startup()
+_wifi_rfkill_set_all_to()
 {
-    _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
+    local status="$1"
+    _arith_bool $(( status == 0 || status == 1 )) || return 1
+    for radio in $(_wifi_get_devices)
+    do
+        uci set "wireless.${radio}.disabled=${status}"
+    done
+    uci commit
+    /sbin/wifi
 }
 
-check_cron_status()
-{
-    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
-}
+_wifi_rfkill_unblock_all() { _wifi_rfkill_set_all_to 0 ;}
+_wifi_rfkill_block_all()   { _wifi_rfkill_set_all_to 1 ;}
 
-disable_wifi()
+wifi_disable()
 {
-    _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
+    _wifi_rfkill_block_all
+    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 >/dev/null 2>&1 ; then
+        _log "info" "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 ignore_stations_filter
+    ignore_stations=$(_uci_get_value_raw "${GLOBAL}.ignore_stations")
+    # shellcheck disable=SC2086
+    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}"
+            # shellcheck disable=SC2086
+            stations=$(printf "%s\n" ${stations} | grep -vwi -E "${ignore_stations_filter}")
+            # shellcheck disable=SC2086
+            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
+
+        # shellcheck disable=SC2086
+        _log "info" "Clients connected on '${_if}': $(_join_by_char ' ' ${stations})" || true
+        if test -n "${ignored_stations}"
+        then
+        # shellcheck disable=SC2086
+        _log "info" "Clients ignored on   '${_if}': $(_join_by_char ' ' ${ignored_stations})" || true
         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 "info" "Do not disable wifi since there is an allow timewindow, skip rechecking."
+        else
+            _log "notice" "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"
+        _log "notice" "Could not disable wifi due to associated stations, retrying..."
+        local recheck_interval
+        recheck_interval=$(_uci_get_value "${GLOBAL}.recheck_interval")
+        if test -n "${recheck_interval}" && _arith_bool $(( recheck_interval > 0 )) ; then
+            _crontab_append_line "*/${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
+    _crontab_rm_script_entries_by_arg "recheck"
+    _cron_restart
+    if _cfg_global_is_unload_modules_enabled
+    then
+        _load_modules
+    fi
+    _wifi_rfkill_unblock_all
 }
 
-enable_wifi()
+wifi_startup()
 {
-    _rm_cron_script "${SCRIPT} recheck"
-    local unload_modules
-    unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
-    if [[ "${unload_modules}" == "1" ]]; then
-        _load_modules
+    _cfg_global_is_enabled || return
+
+    if _cfg_can_wifi_run_now
+    then
+        _log "notice" "enable wifi"
+        wifi_enable
+    else
+        _log "notice" "disable wifi"
+        wifi_disable
     fi
-    _set_status_wifi_uci 0
-    /sbin/wifi
 }
 
 usage()
 {
-    echo ""
     echo "$0 cron|start|startup|stop|forcestop|recheck|getmodules|savemodules|help"
     echo ""
     echo "    UCI Config File: /etc/config/${PACKAGE}"
@@ -394,34 +439,40 @@ usage()
     echo ""
 }
 
+# shellcheck disable=SC2317
 _cleanup()
 {
-    lock -u ${LOCKFILE}
-    rm ${LOCKFILE}
+    lock -u "${LOCKFILE}"
+    rm "${LOCKFILE}"
 }
 
 ###############################################################################
 # MAIN
 ###############################################################################
-trap _cleanup EXIT
-
-LOGGING=$(_get_uci_value ${GLOBAL}.logging) || _exit 1
-_log ${SCRIPT} $1 $2
-lock ${LOCKFILE}
-
-case "$1" in
-    cron) 
-        check_cron_status
-        startup
-    ;;
-    start) enable_wifi ;;
-    startup) startup ;;
-    forcestop) disable_wifi ;;
-    stop) soft_disable_wifi ;;
-    recheck) soft_disable_wifi ;;
-    getmodules) get_module_list ;;
-    savemodules) save_module_list_uci ;;
-    help|--help|-h|*) usage ;;
-esac
-
-_exit 0
+main() {
+    trap _cleanup EXIT
+
+    LOGGING=$(_uci_get_value "${GLOBAL}.logging") || _exit 1
+    _log "info" "${SCRIPT}" "$@"
+    lock "${LOCKFILE}"
+
+    case "$1" in
+        cron)
+            _crontab_reset_from_cfg
+            _cron_restart
+            wifi_startup
+            ;;
+        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 ;;
+    esac
+
+    _exit 0
+}
+
+main "${@}"