#!/bin/bash set -e SCRIPT_URL="https://git.zakscode.com/ztimson/fallback-ap/raw/branch/master/fallback-ap.sh" INSTALL_PATH="/usr/local/bin/fallback-ap" if [ "$EUID" -ne 0 ]; then echo "This installer needs to run as root, e.g.:" echo " curl -sL https://git.zakscode.com/ztimson/fallback-ap/raw/branch/master/install.sh | sudo bash" exit 1 fi # Ask a question; Enter accepts the default. prompt() { local __var="$1" __question="$2" __default="$3" __answer="" read -r -p "$__question [$__default]: " __answer < /dev/tty || __answer="" printf -v "$__var" '%s' "${__answer:-$__default}" } # Like prompt, but "blank" is a legitimate value. Enter accepts the default; # typing the clear word (e.g. "all" or "none") sets the value to empty. prompt_blankable() { local __var="$1" __question="$2" __default="$3" __clear="$4" __answer="" read -r -p "$__question [${__default:-$__clear}]: " __answer < /dev/tty || __answer="" 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//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..." curl -fsSL "$SCRIPT_URL" -o "$INSTALL_PATH" chmod +x "$INSTALL_PATH" echo "Running installer..." "$INSTALL_PATH" install