diff --git a/spoke/setup-network.sh b/spoke/setup-network.sh index a8f9b53..450143c 100755 --- a/spoke/setup-network.sh +++ b/spoke/setup-network.sh @@ -34,6 +34,7 @@ echo " 0) Change hostname" echo " 1) Configure static IP" echo " 2) Prefer IPv4 over IPv6" echo " 3) Prefer IPv6 over IPv4" +echo " 4) Change Wireless Network" echo " q) Quit" echo "" read -rp "Choose: " NET_OPT @@ -71,6 +72,113 @@ case "$NET_OPT" in info "IPv4 preference removed. System will use default IPv6-first behavior." exit 0 ;; +4) + header "Change Wireless Network" + check_deps iw wpa_cli wpa_passphrase + + WIFI_IFACE=$(iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1) + [ -n "$WIFI_IFACE" ] || die "No wireless interface found." + + CURRENT_SSID=$(iw dev "$WIFI_IFACE" link 2>/dev/null | awk '/SSID:/{print $2}') + + info "Scanning for networks on ${WIFI_IFACE}..." + ip link set "$WIFI_IFACE" up 2>/dev/null || true + iw dev "$WIFI_IFACE" scan >/dev/null 2>&1 || true + sleep 2 + + mapfile -t SCAN_LINES < <(iw dev "$WIFI_IFACE" scan 2>/dev/null \ + | awk ' + /^BSS / { signal=""; ssid="" } + /signal:/ { signal=$2 } + /SSID:/ { ssid=substr($0, index($0,$2)); gsub(/^[[:space:]]+|[[:space:]]+$/, "", ssid) } + ssid!="" && signal!="" { print signal "\t" ssid; signal=""; ssid="" } + ' \ + | sort -rn \ + | awk -F'\t' '!seen[$2]++ && $2!="" {print $2}') + + [ ${#SCAN_LINES[@]} -gt 0 ] || die "No wireless networks found. Ensure the interface is up and try again." + + echo "" + for i in "${!SCAN_LINES[@]}"; do + SSID="${SCAN_LINES[$i]}" + if [ "$SSID" = "$CURRENT_SSID" ]; then + echo -e " $((i+1))) ${GREEN}${SSID}${NC} ${CYAN}(connected)${NC}" + else + echo -e " $((i+1))) ${SSID}" + fi + done + echo "" + + read -rp "Enter network number to join: " WIFI_CHOICE + [[ "$WIFI_CHOICE" =~ ^[0-9]+$ ]] || die "Invalid selection." + WIFI_IDX=$((WIFI_CHOICE - 1)) + [ "$WIFI_IDX" -ge 0 ] && [ "$WIFI_IDX" -lt "${#SCAN_LINES[@]}" ] || die "Selection out of range." + + NEW_SSID="${SCAN_LINES[$WIFI_IDX]}" + echo "" + read -rsp "Password for '${NEW_SSID}': " NEW_PASS + echo "" + [ -n "$NEW_PASS" ] || die "Password cannot be empty." + + WPA_CONF=$(wpa_passphrase "$NEW_SSID" "$NEW_PASS") \ + || die "Failed to generate WPA config — check SSID and password." + + NETWORK_ID=$(wpa_cli -i "$WIFI_IFACE" add_network 2>/dev/null | tr -d '[:space:]') + [[ "$NETWORK_ID" =~ ^[0-9]+$ ]] || die "wpa_supplicant is not running on ${WIFI_IFACE}. Start it first." + + PSK=$(echo "$WPA_CONF" | awk -F= '/^\s*psk=/{print $2}' | grep -v '"' | tr -d '[:space:]') + + wpa_cli -i "$WIFI_IFACE" set_network "$NETWORK_ID" ssid "\"${NEW_SSID}\"" >/dev/null + wpa_cli -i "$WIFI_IFACE" set_network "$NETWORK_ID" psk "${PSK}" >/dev/null + wpa_cli -i "$WIFI_IFACE" enable_network "$NETWORK_ID" >/dev/null + wpa_cli -i "$WIFI_IFACE" select_network "$NETWORK_ID" >/dev/null + + info "Waiting for association..." + ASSOCIATED=false + for i in $(seq 1 10); do + sleep 2 + STATUS=$(wpa_cli -i "$WIFI_IFACE" status 2>/dev/null | awk -F= '/^wpa_state=/{print $2}') + if [ "$STATUS" = "COMPLETED" ]; then + ASSOCIATED=true + break + fi + warn "Attempt $i/10 — state: ${STATUS:-unknown}" + done + + if [ "$ASSOCIATED" = "false" ]; then + wpa_cli -i "$WIFI_IFACE" remove_network "$NETWORK_ID" >/dev/null 2>&1 || true + die "Failed to associate with '${NEW_SSID}'. Check the password and try again." + fi + + wpa_cli -i "$WIFI_IFACE" save_config >/dev/null 2>&1 || true + + NETPLAN_FILE=$(ls /etc/netplan/*.yaml 2>/dev/null | head -1) + if [ -n "$NETPLAN_FILE" ] && grep -q "access-points" "$NETPLAN_FILE" 2>/dev/null; then + warn "Updating netplan config with new WiFi credentials..." + NETPLAN_BACKUP_DIR="/root/.config/tinyboard/netplan-backups" + mkdir -p "$NETPLAN_BACKUP_DIR" + BACKUP_FILE="$NETPLAN_BACKUP_DIR/$(basename "${NETPLAN_FILE}").$(date +%Y%m%d%H%M%S)" + cp "$NETPLAN_FILE" "$BACKUP_FILE" + info "Netplan config backed up to $BACKUP_FILE" + + python3 - "$NETPLAN_FILE" "$NEW_SSID" "$NEW_PASS" <<'PYEOF' +import sys, re + +path, ssid, pw = sys.argv[1], sys.argv[2], sys.argv[3] +txt = open(path).read() +txt = re.sub( + r'(access-points:\s*\n\s+)["\']?[^"\':\n]+["\']?:\s*\n(\s+password:)[^\n]*', + lambda m: f'{m.group(1)}"{ssid}":\n{m.group(2)} "{pw}"', + txt +) +open(path, "w").write(txt) +PYEOF + info "Netplan config updated. Changes will persist on next boot." + fi + + info "Connected to '${NEW_SSID}' successfully." + exit 0 + ;; q|Q) exit 0 ;;