generated from ztimson/template
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2769aa98 | ||
|
|
d3e1ab52a9 | ||
|
|
14b08ed70b | ||
|
|
09387088df | ||
|
|
14b8eefb77 | ||
|
|
d1e1842052 | ||
|
|
ca3f2d15a8 | ||
|
|
1107712e75 | ||
|
|
bd19cc87e3 | ||
|
|
1e4b9394db | ||
|
|
b83210d0eb | ||
|
|
9d2987323e | ||
|
|
b319455ef3 |
+468
-54
@@ -4,8 +4,9 @@ set -e
|
|||||||
|
|
||||||
# Configuration =======================================================================================================
|
# Configuration =======================================================================================================
|
||||||
#
|
#
|
||||||
# Connection settings (SSID, passphrase, subnet, DHCP range, interface) can
|
# Connection settings (SSID, passphrase, subnet, DHCP range, interface,
|
||||||
# be supplied three ways, highest priority first:
|
# monitor interfaces, check mode) can be supplied three ways, highest
|
||||||
|
# priority first:
|
||||||
# 1. Environment variables (FALLBACK_AP_*) - used for headless installs.
|
# 1. Environment variables (FALLBACK_AP_*) - used for headless installs.
|
||||||
# 2. The persisted config file below, written by `install`.
|
# 2. The persisted config file below, written by `install`.
|
||||||
# 3. The hard-coded defaults in this block.
|
# 3. The hard-coded defaults in this block.
|
||||||
@@ -16,21 +17,59 @@ set -e
|
|||||||
CONFIG_FILE="/etc/fallback-ap.conf"
|
CONFIG_FILE="/etc/fallback-ap.conf"
|
||||||
if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE"; fi
|
if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE"; fi
|
||||||
|
|
||||||
|
# The interface the AP is created on. The AP is NEVER created on any other
|
||||||
|
# interface, regardless of what is being monitored.
|
||||||
WLAN0_IFACE="${FALLBACK_AP_IFACE:-${SAVED_IFACE:-wlan0}}"
|
WLAN0_IFACE="${FALLBACK_AP_IFACE:-${SAVED_IFACE:-wlan0}}"
|
||||||
|
|
||||||
|
# Which interfaces to watch for connectivity. If any of them is online, the
|
||||||
|
# AP stays off. Comma-separated list (e.g. "eth0,wlan1,wwan0").
|
||||||
|
# Leave BLANK to monitor all REAL interfaces: physical ethernet, wifi and
|
||||||
|
# cellular modems. Virtual interfaces (docker0, br-*, veth*, virbr*, tun/tap,
|
||||||
|
# wireguard, vlan, bridges, bonds, ...) are detected automatically and never
|
||||||
|
# count, so Docker/LXC/VM networking cannot keep the AP off.
|
||||||
|
# Note the missing colon in the expansion: an explicitly empty
|
||||||
|
# FALLBACK_AP_MONITOR_IFACE="" overrides a value saved in the config file.
|
||||||
|
MONITOR_IFACE="${FALLBACK_AP_MONITOR_IFACE-${SAVED_MONITOR_IFACE-}}"
|
||||||
|
MONITOR_IFACE="${MONITOR_IFACE// /}"
|
||||||
|
|
||||||
|
# Extra interfaces to ignore when monitoring all (comma-separated, globs
|
||||||
|
# allowed, e.g. "enx*,usb0"). Only needed for physical NICs you do not want
|
||||||
|
# to count; virtual interfaces are already excluded. Has no effect when
|
||||||
|
# MONITOR_IFACE names interfaces explicitly.
|
||||||
|
IGNORE_IFACE="${FALLBACK_AP_IGNORE_IFACE-${SAVED_IGNORE_IFACE-}}"
|
||||||
|
IGNORE_IFACE="${IGNORE_IFACE// /}"
|
||||||
|
|
||||||
|
# What "online" means:
|
||||||
|
# link - (default) a monitored interface is connected. An outage here is
|
||||||
|
# local and fixable; an upstream outage is not our problem, so
|
||||||
|
# the AP stays off. Fast and never flaps.
|
||||||
|
# internet - a monitored interface is connected AND can ping a target
|
||||||
|
# out of that specific interface (also detects dead uplinks)
|
||||||
|
CHECK_MODE="${FALLBACK_AP_CHECK_MODE:-${SAVED_CHECK_MODE:-link}}"
|
||||||
|
|
||||||
|
# Hosts pinged (space-separated, any one answering counts) in internet mode.
|
||||||
|
CHECK_TARGETS="${FALLBACK_AP_CHECK_TARGETS:-${SAVED_CHECK_TARGETS:-1.1.1.1 8.8.8.8}}"
|
||||||
|
|
||||||
AP_NAME="${FALLBACK_AP_SSID:-${SAVED_SSID:-$HOSTNAME}}"
|
AP_NAME="${FALLBACK_AP_SSID:-${SAVED_SSID:-$HOSTNAME}}"
|
||||||
AP_PASSWORD="${FALLBACK_AP_PASSWORD:-${SAVED_PASSWORD:-abc123}}"
|
AP_PASSWORD="${FALLBACK_AP_PASSWORD:-${SAVED_PASSWORD:-abcd1234}}"
|
||||||
AP_ENCRYPTION="${FALLBACK_AP_ENCRYPTION:-${SAVED_ENCRYPTION:-wpa-psk}}" # none | wpa-psk | sae
|
AP_ENCRYPTION="${FALLBACK_AP_ENCRYPTION:-${SAVED_ENCRYPTION:-wpa-psk}}" # none | wpa-psk | sae
|
||||||
|
|
||||||
FALLBACK_AP_CIDR="${FALLBACK_AP_CIDR:-${SAVED_CIDR:-10.10.10.1/24}}"
|
FALLBACK_AP_CIDR="${FALLBACK_AP_CIDR:-${SAVED_CIDR:-10.10.10.1/24}}"
|
||||||
FALLBACK_AP_DHCP_START_OCTET="${FALLBACK_AP_DHCP_START_OCTET:-${SAVED_DHCP_START_OCTET:-100}}"
|
FALLBACK_AP_DHCP_START_OCTET="${FALLBACK_AP_DHCP_START_OCTET:-${SAVED_DHCP_START_OCTET:-100}}"
|
||||||
FALLBACK_AP_DHCP_END_OCTET="${FALLBACK_AP_DHCP_END_OCTET:-${SAVED_DHCP_END_OCTET:-250}}"
|
FALLBACK_AP_DHCP_END_OCTET="${FALLBACK_AP_DHCP_END_OCTET:-${SAVED_DHCP_END_OCTET:-250}}"
|
||||||
|
|
||||||
AP_CIDR="$FALLBACK_AP_CIDR"
|
# Values derived from the CIDR / DHCP settings. A function, because the
|
||||||
AP_NETWORK_BASE="${AP_CIDR%%/*}" # e.g. "10.10.10.1" out of "10.10.10.1/24"
|
# `install` command-line flags can change those settings and need to
|
||||||
AP_NETWORK_BASE="${AP_NETWORK_BASE%.*}" # e.g. "10.10.10"
|
# recompute these.
|
||||||
DHCP_START="${AP_NETWORK_BASE}.${FALLBACK_AP_DHCP_START_OCTET}"
|
compute_derived() {
|
||||||
DHCP_END="${AP_NETWORK_BASE}.${FALLBACK_AP_DHCP_END_OCTET}"
|
AP_CIDR="$FALLBACK_AP_CIDR"
|
||||||
|
AP_NETWORK_BASE="${AP_CIDR%%/*}" # e.g. "10.10.10.1" out of "10.10.10.1/24"
|
||||||
|
AP_NETWORK_BASE="${AP_NETWORK_BASE%.*}" # e.g. "10.10.10"
|
||||||
|
DHCP_START="${AP_NETWORK_BASE}.${FALLBACK_AP_DHCP_START_OCTET}"
|
||||||
|
DHCP_END="${AP_NETWORK_BASE}.${FALLBACK_AP_DHCP_END_OCTET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_derived
|
||||||
|
|
||||||
# How often, while the AP has no clients, to stop the AP and give
|
# How often, while the AP has no clients, to stop the AP and give
|
||||||
# NetworkManager a chance to reconnect to a known Wi-Fi network.
|
# NetworkManager a chance to reconnect to a known Wi-Fi network.
|
||||||
@@ -44,6 +83,13 @@ RECOVERY_WINDOW=20
|
|||||||
# we query it again.
|
# we query it again.
|
||||||
STATE_SETTLE_DELAY=1
|
STATE_SETTLE_DELAY=1
|
||||||
|
|
||||||
|
# Internet mode only: how often to run the ping checks, how many consecutive
|
||||||
|
# failed checks are needed before declaring "offline" (prevents the AP from
|
||||||
|
# flapping on a single dropped ping), and the per-ping timeout in seconds.
|
||||||
|
ONLINE_CHECK_INTERVAL=5
|
||||||
|
FAIL_THRESHOLD=3
|
||||||
|
PING_TIMEOUT=2
|
||||||
|
|
||||||
CONNECTION_NAME="Fallback AP"
|
CONNECTION_NAME="Fallback AP"
|
||||||
SERVICE_NAME="fallback-ap"
|
SERVICE_NAME="fallback-ap"
|
||||||
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"
|
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"
|
||||||
@@ -56,6 +102,18 @@ STATE_FILE="$STATE_DIR/state"
|
|||||||
# End of Configuration ================================================================================================
|
# End of Configuration ================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
validate_check_mode() {
|
||||||
|
case "$CHECK_MODE" in
|
||||||
|
link|internet) ;;
|
||||||
|
*)
|
||||||
|
echo "Invalid check mode '$CHECK_MODE' (use: link | internet)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_check_mode
|
||||||
|
|
||||||
# Resolve the actual location of this script.
|
# Resolve the actual location of this script.
|
||||||
SCRIPT_PATH="$(readlink -f "$0")"
|
SCRIPT_PATH="$(readlink -f "$0")"
|
||||||
|
|
||||||
@@ -86,17 +144,210 @@ is_ap_on() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
is_wifi_connected() {
|
# --- Connectivity monitoring -----------------------------------------------------------------------------------------
|
||||||
nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status 2>/dev/null |
|
|
||||||
awk -F: -v iface="$WLAN0_IFACE" -v ap="$CONNECTION_NAME" \
|
# True if no monitor interface is configured (= monitor everything).
|
||||||
'$1 == iface && $2 == "wifi" && $3 == "connected" && $4 != ap {found=1} END {exit !found}'
|
monitor_all() {
|
||||||
|
[ -z "$MONITOR_IFACE" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# True if the AP interface is among the monitored interfaces. If it is not,
|
||||||
|
# a "recovery attempt" (taking the AP down to let wlan0 reconnect to Wi-Fi)
|
||||||
|
# could never change the result, so it is skipped.
|
||||||
|
monitors_wlan0() {
|
||||||
|
if monitor_all; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case ",${MONITOR_IFACE}," in
|
||||||
|
*",${WLAN0_IFACE},"*) return 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# A "real" interface is backed by hardware. Linux exposes this as a `device`
|
||||||
|
# link under /sys/class/net/<iface>/, which physical NICs (PCI, USB, SDIO,
|
||||||
|
# virtio, ...) have and virtual ones (docker0, veth*, br-*, tun*, wg*, vlan,
|
||||||
|
# bridges, bonds, dummy) do not. Cellular modems are the exception: NetworkManager
|
||||||
|
# names them after their control port (e.g. cdc-wdm0), which is not in
|
||||||
|
# /sys/class/net, so gsm/cdma types are accepted as-is.
|
||||||
|
is_real_interface() {
|
||||||
|
local dev="$1"
|
||||||
|
local type="$2"
|
||||||
|
|
||||||
|
case "$type" in
|
||||||
|
gsm|cdma) return 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -e "/sys/class/net/$dev/device" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# True if the device matches an entry of IGNORE_IFACE (globs allowed).
|
||||||
|
is_ignored_interface() {
|
||||||
|
local dev="$1"
|
||||||
|
local pattern
|
||||||
|
local -a patterns=()
|
||||||
|
|
||||||
|
IFS=',' read -r -a patterns <<< "$IGNORE_IFACE" || true
|
||||||
|
|
||||||
|
for pattern in "${patterns[@]}"; do
|
||||||
|
if [ -n "$pattern" ] && [[ "$dev" == $pattern ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Prints (one per line) the monitored devices that NetworkManager reports as
|
||||||
|
# connected, excluding the fallback AP connection itself.
|
||||||
|
# - Blank MONITOR_IFACE: every real (hardware-backed) ethernet, wifi or
|
||||||
|
# cellular device that is not in IGNORE_IFACE.
|
||||||
|
# - Otherwise: only the devices named in MONITOR_IFACE (any type).
|
||||||
|
# The state is matched with a prefix because nmcli reports values such as
|
||||||
|
# "connected (externally)" or "connected (site only)".
|
||||||
|
get_monitored_devices() {
|
||||||
|
local dev type
|
||||||
|
local all=0
|
||||||
|
|
||||||
|
if monitor_all; then
|
||||||
|
all=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS=: read -r dev type; do
|
||||||
|
if [ -z "$dev" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$all" -eq 1 ]; then
|
||||||
|
case "$type" in
|
||||||
|
ethernet|wifi|gsm|cdma) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if ! is_real_interface "$dev" "$type"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_ignored_interface "$dev"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
case ",${MONITOR_IFACE}," in
|
||||||
|
*",${dev},"*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$dev"
|
||||||
|
done < <(
|
||||||
|
nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status 2>/dev/null |
|
||||||
|
awk -F: -v ap="$CONNECTION_NAME" \
|
||||||
|
'$3 ~ /^connected/ && $4 != ap {print $1 ":" $2}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Can this specific interface reach the internet? Pings are forced out of the
|
||||||
|
# given interface (-I) so another interface cannot answer on its behalf.
|
||||||
|
iface_has_internet() {
|
||||||
|
local dev="$1"
|
||||||
|
local target
|
||||||
|
|
||||||
|
for target in $CHECK_TARGETS; do
|
||||||
|
if ping -I "$dev" -c 1 -W "$PING_TIMEOUT" "$target" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Is ANY monitored interface online right now (according to CHECK_MODE)?
|
||||||
|
check_online() {
|
||||||
|
local dev
|
||||||
|
|
||||||
|
while read -r dev; do
|
||||||
|
if [ -z "$dev" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CHECK_MODE" = "link" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if iface_has_internet "$dev"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < <(get_monitored_devices)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Debounced wrapper around check_online, used by the main loop. Updates the
|
||||||
|
# `online`, `fail_count` and `last_online_check` variables of run().
|
||||||
|
# link mode: checked every tick, no debounce (cheap).
|
||||||
|
# internet mode: checked every ONLINE_CHECK_INTERVAL seconds, and only
|
||||||
|
# declared offline after FAIL_THRESHOLD consecutive failures.
|
||||||
|
# Coming back online is detected on the first success.
|
||||||
|
update_online() {
|
||||||
|
local now="$1"
|
||||||
|
local interval=0
|
||||||
|
local threshold=1
|
||||||
|
|
||||||
|
if [ "$CHECK_MODE" = "internet" ]; then
|
||||||
|
interval="$ONLINE_CHECK_INTERVAL"
|
||||||
|
threshold="$FAIL_THRESHOLD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( now - last_online_check < interval )); then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
last_online_check="$now"
|
||||||
|
|
||||||
|
if check_online; then
|
||||||
|
fail_count=0
|
||||||
|
online=1
|
||||||
|
else
|
||||||
|
fail_count=$((fail_count + 1))
|
||||||
|
|
||||||
|
if (( fail_count >= threshold )); then
|
||||||
|
online=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_online() {
|
||||||
|
local timeout="$1"
|
||||||
|
local i
|
||||||
|
|
||||||
|
for ((i=0; i<timeout; i++)); do
|
||||||
|
if check_online; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Wi-Fi bookkeeping -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
get_wifi_connection() {
|
get_wifi_connection() {
|
||||||
nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status 2>/dev/null |
|
nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status 2>/dev/null |
|
||||||
awk -F: -v iface="$WLAN0_IFACE" \
|
awk -F: -v iface="$WLAN0_IFACE" \
|
||||||
'$1 == iface && $2 == "wifi" && $3 == "connected" {print $4; exit}'
|
'$1 == iface && $2 == "wifi" && $3 ~ /^connected/ {print $4; exit}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -148,27 +399,14 @@ wait_for_ap_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wait_for_wifi() {
|
# --- AP control (always on $WLAN0_IFACE only) ------------------------------------------------------------------------
|
||||||
local timeout="$1"
|
|
||||||
|
|
||||||
for ((i=0; i<timeout*10; i++)); do
|
|
||||||
if is_wifi_connected; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
start_ap() {
|
start_ap() {
|
||||||
if is_ap_on; then
|
if is_ap_on; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Enabling AP: $AP_NAME"
|
log "Enabling AP: $AP_NAME (on $WLAN0_IFACE)"
|
||||||
|
|
||||||
nmcli connection up "$CONNECTION_NAME" ifname "$WLAN0_IFACE" >/dev/null 2>&1 || {
|
nmcli connection up "$CONNECTION_NAME" ifname "$WLAN0_IFACE" >/dev/null 2>&1 || {
|
||||||
log "Failed to start AP"
|
log "Failed to start AP"
|
||||||
@@ -207,23 +445,32 @@ stop_ap() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Main loop logic -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Called once per second from run(). Relies on run()'s local variables
|
||||||
|
# (last_attempt, online, fail_count, last_online_check) via dynamic scoping.
|
||||||
tick() {
|
tick() {
|
||||||
local now="$1"
|
local now="$1"
|
||||||
|
|
||||||
if is_wifi_connected; then
|
update_online "$now"
|
||||||
|
|
||||||
|
if (( online )); then
|
||||||
remember_wifi
|
remember_wifi
|
||||||
|
|
||||||
if is_ap_on; then
|
if is_ap_on; then
|
||||||
stop_ap
|
log "Connectivity available, shutting down fallback AP"
|
||||||
|
stop_ap || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set_state "wifi"
|
set_state "online"
|
||||||
last_attempt="$now"
|
last_attempt="$now"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! is_ap_on; then
|
if ! is_ap_on; then
|
||||||
start_ap
|
log "No connectivity on monitored interface(s), starting fallback AP"
|
||||||
|
start_ap || true
|
||||||
set_state "fallback"
|
set_state "fallback"
|
||||||
last_attempt="$now"
|
last_attempt="$now"
|
||||||
return
|
return
|
||||||
@@ -239,14 +486,20 @@ tick() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Recovery only makes sense if wlan0 is one of the monitored interfaces:
|
||||||
|
# it works by briefly freeing wlan0 so it can reconnect to known Wi-Fi.
|
||||||
|
if ! monitors_wlan0; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
if (( now - last_attempt >= RECOVERY_CHECK_INTERVAL )); then
|
if (( now - last_attempt >= RECOVERY_CHECK_INTERVAL )); then
|
||||||
last_attempt="$now"
|
last_attempt="$now"
|
||||||
attempt_recovery
|
attempt_recovery || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt_recovery() {
|
attempt_recovery() {
|
||||||
log "No AP clients, checking for known Wi-Fi (AP down for up to ${RECOVERY_WINDOW}s)"
|
log "No AP clients, checking for connectivity (AP down for up to ${RECOVERY_WINDOW}s)"
|
||||||
|
|
||||||
set_state "checking"
|
set_state "checking"
|
||||||
|
|
||||||
@@ -254,25 +507,27 @@ attempt_recovery() {
|
|||||||
|
|
||||||
if is_ap_on; then
|
if is_ap_on; then
|
||||||
log "AP did not go down, aborting recovery attempt"
|
log "AP did not go down, aborting recovery attempt"
|
||||||
start_ap
|
start_ap || true
|
||||||
set_state "fallback"
|
set_state "fallback"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if wait_for_wifi "$RECOVERY_WINDOW"; then
|
if wait_for_online "$RECOVERY_WINDOW"; then
|
||||||
sleep "$STATE_SETTLE_DELAY"
|
sleep "$STATE_SETTLE_DELAY"
|
||||||
|
|
||||||
if is_wifi_connected; then
|
if check_online; then
|
||||||
log "Wi-Fi reconnected"
|
log "Connectivity restored"
|
||||||
remember_wifi
|
remember_wifi
|
||||||
set_state "wifi"
|
online=1
|
||||||
|
fail_count=0
|
||||||
|
set_state "online"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "No Wi-Fi available, restoring AP"
|
log "No connectivity available, restoring AP"
|
||||||
|
|
||||||
start_ap
|
start_ap || true
|
||||||
set_state "fallback"
|
set_state "fallback"
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
@@ -283,6 +538,10 @@ save_config() {
|
|||||||
mkdir -p "$(dirname "$CONFIG_FILE")"
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
||||||
cat > "$CONFIG_FILE" <<EOF
|
cat > "$CONFIG_FILE" <<EOF
|
||||||
SAVED_IFACE="$WLAN0_IFACE"
|
SAVED_IFACE="$WLAN0_IFACE"
|
||||||
|
SAVED_MONITOR_IFACE="$MONITOR_IFACE"
|
||||||
|
SAVED_IGNORE_IFACE="$IGNORE_IFACE"
|
||||||
|
SAVED_CHECK_MODE="$CHECK_MODE"
|
||||||
|
SAVED_CHECK_TARGETS="$CHECK_TARGETS"
|
||||||
SAVED_SSID="$AP_NAME"
|
SAVED_SSID="$AP_NAME"
|
||||||
SAVED_PASSWORD="$AP_PASSWORD"
|
SAVED_PASSWORD="$AP_PASSWORD"
|
||||||
SAVED_ENCRYPTION="$AP_ENCRYPTION"
|
SAVED_ENCRYPTION="$AP_ENCRYPTION"
|
||||||
@@ -294,25 +553,115 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
usage_install() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $SCRIPT_PATH install [options]
|
||||||
|
|
||||||
|
Options (each overrides the matching FALLBACK_AP_* env var and any saved value):
|
||||||
|
--iface NAME Interface the AP is created on (default: wlan0)
|
||||||
|
--monitor-iface LIST Comma-separated interfaces to watch for connectivity.
|
||||||
|
Blank ("") monitors all REAL interfaces; virtual ones
|
||||||
|
(docker, veth, bridges, ...) are skipped (default: blank)
|
||||||
|
--ignore-iface LIST Comma-separated physical interfaces to ignore when
|
||||||
|
monitoring all; globs allowed, e.g. "enx*"
|
||||||
|
(default: none)
|
||||||
|
--check-mode MODE link | internet (default: link)
|
||||||
|
--check-targets "H.." Space-separated hosts to ping in internet mode
|
||||||
|
(default: "1.1.1.1 8.8.8.8")
|
||||||
|
--ssid NAME AP name (default: hostname)
|
||||||
|
--password PASS AP passphrase (default: abcd1234)
|
||||||
|
--encryption TYPE none | wpa-psk | sae (default: wpa-psk)
|
||||||
|
--cidr ADDR/MASK AP address and subnet (default: 10.10.10.1/24)
|
||||||
|
--dhcp-start OCTET First DHCP address, last octet (default: 100)
|
||||||
|
--dhcp-end OCTET Last DHCP address, last octet (default: 250)
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
Both "--opt value" and "--opt=value" are accepted. Precedence, highest first:
|
||||||
|
flags, FALLBACK_AP_* env vars, saved config file, built-in defaults.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$SCRIPT_PATH install
|
||||||
|
$SCRIPT_PATH install --monitor-iface eth0,wwan0
|
||||||
|
$SCRIPT_PATH install --iface wlan1 --monitor-iface "" --check-mode internet
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse_install_args() {
|
||||||
|
local opt val
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
opt="$1"
|
||||||
|
|
||||||
|
case "$opt" in
|
||||||
|
-h|--help)
|
||||||
|
usage_install
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*=*)
|
||||||
|
val="${opt#*=}"
|
||||||
|
opt="${opt%%=*}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Option $opt requires a value."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
val="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $opt"
|
||||||
|
usage_install
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$opt" in
|
||||||
|
--iface) WLAN0_IFACE="$val" ;;
|
||||||
|
--monitor-iface) MONITOR_IFACE="${val// /}" ;;
|
||||||
|
--ignore-iface) IGNORE_IFACE="${val// /}" ;;
|
||||||
|
--check-mode) CHECK_MODE="$val" ;;
|
||||||
|
--check-targets) CHECK_TARGETS="$val" ;;
|
||||||
|
--ssid) AP_NAME="$val" ;;
|
||||||
|
--password) AP_PASSWORD="$val" ;;
|
||||||
|
--encryption) AP_ENCRYPTION="$val" ;;
|
||||||
|
--cidr) FALLBACK_AP_CIDR="$val" ;;
|
||||||
|
--dhcp-start) FALLBACK_AP_DHCP_START_OCTET="$val" ;;
|
||||||
|
--dhcp-end) FALLBACK_AP_DHCP_END_OCTET="$val" ;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $opt"
|
||||||
|
usage_install
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
validate_check_mode
|
||||||
|
compute_derived
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
install() {
|
install() {
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
echo "Run this script as root."
|
echo "Run this script as root."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
parse_install_args "$@"
|
||||||
|
|
||||||
log "Installing NetworkManager fallback AP"
|
log "Installing NetworkManager fallback AP"
|
||||||
|
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y network-manager iw
|
apt-get install -y network-manager iw iputils-ping
|
||||||
|
|
||||||
systemctl enable NetworkManager
|
systemctl enable NetworkManager
|
||||||
systemctl start NetworkManager
|
systemctl start NetworkManager
|
||||||
|
|
||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
|
|
||||||
if is_wifi_connected; then
|
remember_wifi
|
||||||
remember_wifi
|
|
||||||
fi
|
|
||||||
|
|
||||||
create_connection
|
create_connection
|
||||||
save_config
|
save_config
|
||||||
@@ -345,7 +694,15 @@ EOF
|
|||||||
log "Script: $SCRIPT_PATH"
|
log "Script: $SCRIPT_PATH"
|
||||||
log "Config file: $CONFIG_FILE"
|
log "Config file: $CONFIG_FILE"
|
||||||
log "SSID: $AP_NAME"
|
log "SSID: $AP_NAME"
|
||||||
log "Interface: $WLAN0_IFACE"
|
log "AP interface: $WLAN0_IFACE"
|
||||||
|
log "Monitored interfaces: ${MONITOR_IFACE:-all real interfaces}"
|
||||||
|
if monitor_all && [ -n "$IGNORE_IFACE" ]; then
|
||||||
|
log "Ignored interfaces: $IGNORE_IFACE"
|
||||||
|
fi
|
||||||
|
log "Check mode: $CHECK_MODE"
|
||||||
|
if [ "$CHECK_MODE" = "internet" ]; then
|
||||||
|
log "Ping targets: $CHECK_TARGETS"
|
||||||
|
fi
|
||||||
log "Network: $AP_CIDR"
|
log "Network: $AP_CIDR"
|
||||||
log "DHCP: $DHCP_START - $DHCP_END"
|
log "DHCP: $DHCP_START - $DHCP_END"
|
||||||
log "Encryption: $AP_ENCRYPTION"
|
log "Encryption: $AP_ENCRYPTION"
|
||||||
@@ -484,10 +841,56 @@ status() {
|
|||||||
echo
|
echo
|
||||||
echo "=== Config ($CONFIG_FILE) ==="
|
echo "=== Config ($CONFIG_FILE) ==="
|
||||||
echo "SSID: $AP_NAME"
|
echo "SSID: $AP_NAME"
|
||||||
echo "Interface: $WLAN0_IFACE"
|
echo "AP interface: $WLAN0_IFACE"
|
||||||
|
echo "Monitored interfaces: ${MONITOR_IFACE:-all real interfaces}"
|
||||||
|
if monitor_all && [ -n "$IGNORE_IFACE" ]; then
|
||||||
|
echo "Ignored interfaces: $IGNORE_IFACE"
|
||||||
|
fi
|
||||||
|
echo "Check mode: $CHECK_MODE"
|
||||||
|
if [ "$CHECK_MODE" = "internet" ]; then
|
||||||
|
echo "Ping targets: $CHECK_TARGETS"
|
||||||
|
fi
|
||||||
echo "Network: $AP_CIDR"
|
echo "Network: $AP_CIDR"
|
||||||
echo "DHCP: $DHCP_START - $DHCP_END"
|
echo "DHCP: $DHCP_START - $DHCP_END"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Connectivity ($CHECK_MODE) ==="
|
||||||
|
local dev found=0
|
||||||
|
while read -r dev; do
|
||||||
|
if [ -z "$dev" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
found=1
|
||||||
|
|
||||||
|
if [ "$CHECK_MODE" = "internet" ]; then
|
||||||
|
if iface_has_internet "$dev"; then
|
||||||
|
echo "$dev: internet OK"
|
||||||
|
else
|
||||||
|
echo "$dev: connected, NO internet"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$dev: connected"
|
||||||
|
fi
|
||||||
|
done < <(get_monitored_devices)
|
||||||
|
|
||||||
|
if [ "$found" -eq 0 ]; then
|
||||||
|
echo "No monitored interface is connected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Connected but NOT counted (virtual / ignored / unmonitored) ==="
|
||||||
|
local monitored
|
||||||
|
monitored="$(get_monitored_devices)"
|
||||||
|
nmcli -t -f DEVICE,STATE,CONNECTION device status 2>/dev/null |
|
||||||
|
awk -F: -v ap="$CONNECTION_NAME" \
|
||||||
|
'$2 ~ /^connected/ && $3 != ap {print $1}' |
|
||||||
|
while read -r dev; do
|
||||||
|
if ! grep -Fxq "$dev" <<< "$monitored"; then
|
||||||
|
echo "$dev"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "=== Last Known Wi-Fi ==="
|
echo "=== Last Known Wi-Fi ==="
|
||||||
get_last_wifi || echo "None"
|
get_last_wifi || echo "None"
|
||||||
@@ -507,27 +910,36 @@ status() {
|
|||||||
|
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
|
# These are shared with tick/update_online/attempt_recovery through
|
||||||
|
# dynamic scoping.
|
||||||
local last_attempt=0
|
local last_attempt=0
|
||||||
|
local last_online_check=0
|
||||||
|
local fail_count=0
|
||||||
|
local online=0
|
||||||
local now
|
local now
|
||||||
|
|
||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
|
|
||||||
log "Fallback AP service started"
|
log "Fallback AP service started"
|
||||||
log "Script: $SCRIPT_PATH"
|
log "Script: $SCRIPT_PATH"
|
||||||
|
log "AP interface: $WLAN0_IFACE | monitoring: ${MONITOR_IFACE:-all real interfaces} | mode: $CHECK_MODE"
|
||||||
exec 9>"$LOCK_FILE"
|
exec 9>"$LOCK_FILE"
|
||||||
|
|
||||||
flock 9
|
flock 9
|
||||||
|
|
||||||
if is_wifi_connected; then
|
if check_online; then
|
||||||
|
online=1
|
||||||
remember_wifi
|
remember_wifi
|
||||||
set_state "wifi"
|
set_state "online"
|
||||||
log "Wi-Fi already connected"
|
log "Connectivity already available"
|
||||||
else
|
else
|
||||||
log "No Wi-Fi connection, starting fallback AP"
|
log "No connectivity on monitored interface(s), starting fallback AP"
|
||||||
start_ap
|
start_ap || true
|
||||||
set_state "fallback"
|
set_state "fallback"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
last_online_check="$(date +%s)"
|
||||||
|
|
||||||
flock -u 9
|
flock -u 9
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
@@ -551,7 +963,8 @@ fi
|
|||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
install)
|
install)
|
||||||
install
|
shift
|
||||||
|
install "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
uninstall)
|
uninstall)
|
||||||
@@ -575,7 +988,8 @@ case "${1:-}" in
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "Usage: $SCRIPT_PATH {install|uninstall|start|stop|status|run}"
|
echo "Usage: $SCRIPT_PATH {install [options]|uninstall|start|stop|status|run}"
|
||||||
|
echo " $SCRIPT_PATH install --help for install options"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
+125
-10
@@ -11,24 +11,139 @@ if [ "$EUID" -ne 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ask a question; Enter accepts the default.
|
||||||
prompt() {
|
prompt() {
|
||||||
local __var="$1" __question="$2" __default="$3" __answer=""
|
local __var="$1" __question="$2" __default="$3" __answer=""
|
||||||
if [ -e /dev/tty ]; then
|
read -r -p "$__question [$__default]: " __answer < /dev/tty || __answer=""
|
||||||
read -r -p "$__question [$__default]: " __answer < /dev/tty || __answer=""
|
|
||||||
fi
|
|
||||||
printf -v "$__var" '%s' "${__answer:-$__default}"
|
printf -v "$__var" '%s' "${__answer:-$__default}"
|
||||||
}
|
}
|
||||||
|
|
||||||
[ -z "${FALLBACK_AP_SSID:-}" ] && prompt FALLBACK_AP_SSID "Access point SSID" "$(hostname)"
|
# Like prompt, but "blank" is a legitimate value. Enter accepts the default;
|
||||||
[ -z "${FALLBACK_AP_PASSWORD:-}" ] && prompt FALLBACK_AP_PASSWORD "Access point passphrase" "abc123"
|
# typing the clear word (e.g. "all" or "none") sets the value to empty.
|
||||||
[ -z "${FALLBACK_AP_CIDR:-}" ] && prompt FALLBACK_AP_CIDR "Access point subnet (CIDR)" "10.10.10.1/24"
|
prompt_blankable() {
|
||||||
[ -z "${FALLBACK_AP_DHCP_START_OCTET:-}" ] && prompt FALLBACK_AP_DHCP_START_OCTET "DHCP range start (last octet)" "100"
|
local __var="$1" __question="$2" __default="$3" __clear="$4" __answer=""
|
||||||
[ -z "${FALLBACK_AP_DHCP_END_OCTET:-}" ] && prompt FALLBACK_AP_DHCP_END_OCTET "DHCP range end (last octet)" "250"
|
read -r -p "$__question [${__default:-$__clear}]: " __answer < /dev/tty || __answer=""
|
||||||
|
|
||||||
export FALLBACK_AP_SSID FALLBACK_AP_PASSWORD FALLBACK_AP_CIDR FALLBACK_AP_DHCP_START_OCTET FALLBACK_AP_DHCP_END_OCTET
|
case "$__answer" in
|
||||||
|
"") __answer="$__default" ;;
|
||||||
|
"$__clear") __answer="" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf -v "$__var" '%s' "${__answer// /}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ask until the answer is one of the allowed choices. Enter accepts the default.
|
||||||
|
prompt_choice() {
|
||||||
|
local __var="$1" __question="$2" __default="$3" __answer="" __opt
|
||||||
|
shift 3
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -r -p "$__question [$__default]: " __answer < /dev/tty || __answer=""
|
||||||
|
__answer="${__answer:-$__default}"
|
||||||
|
|
||||||
|
for __opt in "$@"; do
|
||||||
|
if [ "$__answer" = "$__opt" ]; then
|
||||||
|
printf -v "$__var" '%s' "$__answer"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo " Please enter one of: $*"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lists wireless interfaces (those with a /sys/class/net/<iface>/wireless dir).
|
||||||
|
detect_wireless() {
|
||||||
|
local dir
|
||||||
|
for dir in /sys/class/net/*; do
|
||||||
|
if [ -d "$dir/wireless" ]; then
|
||||||
|
basename "$dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
EXISTING_CONFIG="/etc/fallback-ap.conf"
|
||||||
|
|
||||||
|
if [ -f "$EXISTING_CONFIG" ]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$EXISTING_CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DETECTED_WIFI="$(detect_wireless | tr '\n' ' ')"
|
||||||
|
DETECTED_WIFI="${DETECTED_WIFI% }"
|
||||||
|
|
||||||
|
DEFAULT_SSID="${SAVED_SSID:-$(hostname)}"
|
||||||
|
DEFAULT_PASSWORD="${SAVED_PASSWORD:-abcd1234}"
|
||||||
|
DEFAULT_IFACE="${SAVED_IFACE:-${DETECTED_WIFI%% *}}"
|
||||||
|
DEFAULT_IFACE="${DEFAULT_IFACE:-wlan0}"
|
||||||
|
DEFAULT_CIDR="${SAVED_CIDR:-10.10.10.1/24}"
|
||||||
|
DEFAULT_DHCP_START_OCTET="${SAVED_DHCP_START_OCTET:-100}"
|
||||||
|
DEFAULT_DHCP_END_OCTET="${SAVED_DHCP_END_OCTET:-250}"
|
||||||
|
DEFAULT_MONITOR_IFACE="${SAVED_MONITOR_IFACE-}"
|
||||||
|
DEFAULT_IGNORE_IFACE="${SAVED_IGNORE_IFACE-}"
|
||||||
|
DEFAULT_CHECK_MODE="${SAVED_CHECK_MODE:-link}"
|
||||||
|
DEFAULT_CHECK_TARGETS="${SAVED_CHECK_TARGETS:-1.1.1.1 8.8.8.8}"
|
||||||
|
|
||||||
|
# --- Access point ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ -z "${FALLBACK_AP_SSID:-}" ] && prompt FALLBACK_AP_SSID "Access point SSID" "$DEFAULT_SSID"
|
||||||
|
[ -z "${FALLBACK_AP_PASSWORD:-}" ] && prompt FALLBACK_AP_PASSWORD "Access point passphrase" "$DEFAULT_PASSWORD"
|
||||||
|
|
||||||
|
if [ -z "${FALLBACK_AP_IFACE:-}" ]; then
|
||||||
|
if [ -n "$DETECTED_WIFI" ]; then
|
||||||
|
echo "Detected Wi-Fi interfaces: $DETECTED_WIFI"
|
||||||
|
fi
|
||||||
|
prompt FALLBACK_AP_IFACE "Interface to create the access point on" "$DEFAULT_IFACE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -z "${FALLBACK_AP_CIDR:-}" ] && prompt FALLBACK_AP_CIDR "Access point subnet (CIDR)" "$DEFAULT_CIDR"
|
||||||
|
[ -z "${FALLBACK_AP_DHCP_START_OCTET:-}" ] && prompt FALLBACK_AP_DHCP_START_OCTET "DHCP range start (last octet)" "$DEFAULT_DHCP_START_OCTET"
|
||||||
|
[ -z "${FALLBACK_AP_DHCP_END_OCTET:-}" ] && prompt FALLBACK_AP_DHCP_END_OCTET "DHCP range end (last octet)" "$DEFAULT_DHCP_END_OCTET"
|
||||||
|
|
||||||
|
# --- Connectivity monitoring -----------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# The AP only starts when none of the monitored interfaces are online, and it
|
||||||
|
# is only ever created on the interface chosen above.
|
||||||
|
#
|
||||||
|
# These use "unset" (not "empty") to decide whether to ask, because an empty
|
||||||
|
# FALLBACK_AP_MONITOR_IFACE / FALLBACK_AP_IGNORE_IFACE is a valid answer
|
||||||
|
# meaning "all real interfaces" / "ignore nothing".
|
||||||
|
|
||||||
|
if [ -z "${FALLBACK_AP_MONITOR_IFACE+x}" ]; then
|
||||||
|
echo
|
||||||
|
echo "Which interfaces should count as 'online'? Enter a comma-separated list"
|
||||||
|
echo "(e.g. eth0,wlan0), or 'all' for every real interface. Virtual interfaces"
|
||||||
|
echo "(docker, veth, bridges, VPNs) are skipped automatically."
|
||||||
|
prompt_blankable FALLBACK_AP_MONITOR_IFACE "Interfaces to monitor" "$DEFAULT_MONITOR_IFACE" "all"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$FALLBACK_AP_MONITOR_IFACE" ] && [ -z "${FALLBACK_AP_IGNORE_IFACE+x}" ]; then
|
||||||
|
echo
|
||||||
|
echo "Any physical interfaces to ignore (globs allowed, e.g. enx*,usb0)?"
|
||||||
|
prompt_blankable FALLBACK_AP_IGNORE_IFACE "Interfaces to ignore" "$DEFAULT_IGNORE_IFACE" "none"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${FALLBACK_AP_CHECK_MODE:-}" ]; then
|
||||||
|
echo
|
||||||
|
echo "How should 'online' be determined?"
|
||||||
|
echo " link - the interface is connected (an upstream outage keeps the AP off)"
|
||||||
|
echo " internet - the interface is connected AND can ping out"
|
||||||
|
prompt_choice FALLBACK_AP_CHECK_MODE "Check mode" "$DEFAULT_CHECK_MODE" link internet
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$FALLBACK_AP_CHECK_MODE" = "internet" ] && [ -z "${FALLBACK_AP_CHECK_TARGETS:-}" ]; then
|
||||||
|
prompt FALLBACK_AP_CHECK_TARGETS "Hosts to ping (space-separated)" "$DEFAULT_CHECK_TARGETS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export FALLBACK_AP_SSID FALLBACK_AP_PASSWORD FALLBACK_AP_IFACE FALLBACK_AP_CIDR FALLBACK_AP_DHCP_START_OCTET FALLBACK_AP_DHCP_END_OCTET
|
||||||
|
export FALLBACK_AP_CHECK_MODE
|
||||||
|
[ -n "${FALLBACK_AP_MONITOR_IFACE+x}" ] && export FALLBACK_AP_MONITOR_IFACE
|
||||||
|
[ -n "${FALLBACK_AP_IGNORE_IFACE+x}" ] && export FALLBACK_AP_IGNORE_IFACE
|
||||||
|
[ -n "${FALLBACK_AP_CHECK_TARGETS+x}" ] && export FALLBACK_AP_CHECK_TARGETS
|
||||||
|
|
||||||
|
echo
|
||||||
echo "Downloading fallback-ap..."
|
echo "Downloading fallback-ap..."
|
||||||
curl -fsSL "$SCRIPT_URL" -o "$INSTALL_PATH"
|
curl -fsSL "$SCRIPT_URL" -o "$INSTALL_PATH"
|
||||||
chmod +x "$INSTALL_PATH"
|
chmod +x "$INSTALL_PATH"
|
||||||
echo "Running installer..."
|
echo "Running installer..."
|
||||||
"$INSTALL_PATH" install
|
"$INSTALL_PATH" install
|
||||||
Reference in New Issue
Block a user