Skip to content

Instantly share code, notes, and snippets.

@fadlee
Created April 16, 2026 02:59
Show Gist options
  • Select an option

  • Save fadlee/8a2d79243e09a31e47c811240e19a2b6 to your computer and use it in GitHub Desktop.

Select an option

Save fadlee/8a2d79243e09a31e47c811240e19a2b6 to your computer and use it in GitHub Desktop.
Script to switch server wifi safely
#!/usr/bin/env bash
set -Eeuo pipefail
INTERFACE="${INTERFACE:-wlp1s0}"
TARGET_SSID="${TARGET_SSID:-CHANGE_ME_SSID}"
TARGET_PASSWORD="${TARGET_PASSWORD:-CHANGE_ME_PASSWORD}"
TARGET_BSSID="${TARGET_BSSID:-}"
ROLLBACK_DELAY="${ROLLBACK_DELAY:-45}"
VERIFY_SECONDS="${VERIFY_SECONDS:-25}"
PING_HOST="${PING_HOST:-1.1.1.1}"
ROLLBACK_PID=""
ROLLBACK_FILE=""
log() {
printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}
fail() {
log "ERROR: $*"
exit 1
}
require_root() {
[[ "${EUID}" -eq 0 ]] || fail "run as root"
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "missing command: $1"
}
current_connection() {
nmcli -t -f GENERAL.CONNECTION device show "$INTERFACE" 2>/dev/null | head -n1 | cut -d: -f2-
}
connection_exists() {
local name="$1"
[[ -n "$name" && "$name" != "--" ]] || return 1
nmcli -t -f NAME connection show | grep -Fxq "$name"
}
is_healthy() {
local state=""
state=$(nmcli -t -f GENERAL.STATE device show "$INTERFACE" 2>/dev/null | head -n1 | cut -d: -f2-)
[[ "$state" == "100 (connected)" ]] || return 1
ping -I "$INTERFACE" -c 1 -W 3 "$PING_HOST" >/dev/null 2>&1
}
wait_for_health() {
local seconds="$1"
local deadline=$((SECONDS + seconds))
while ((SECONDS < deadline)); do
if is_healthy; then
return 0
fi
sleep 2
done
return 1
}
start_rollback_timer() {
local previous="$1"
connection_exists "$previous" || fail "previous connection not found: $previous"
ROLLBACK_FILE="/tmp/wifi-rollback-${INTERFACE}.flag"
printf 'armed\n' >"$ROLLBACK_FILE"
(
sleep "$ROLLBACK_DELAY"
if [[ -f "$ROLLBACK_FILE" ]]; then
logger -t switch-wifi "rollback timer fired; restoring [$previous] on [$INTERFACE]"
nmcli connection up "$previous" ifname "$INTERFACE" >/tmp/switch-wifi-rollback.log 2>&1 || true
fi
) &
ROLLBACK_PID=$!
log "Rollback armed: ${ROLLBACK_DELAY}s, pid=$ROLLBACK_PID"
}
disarm_rollback_timer() {
[[ -n "${ROLLBACK_FILE:-}" && -f "${ROLLBACK_FILE:-}" ]] && rm -f "$ROLLBACK_FILE" || true
if [[ -n "${ROLLBACK_PID:-}" ]]; then
kill "$ROLLBACK_PID" >/dev/null 2>&1 || true
wait "$ROLLBACK_PID" 2>/dev/null || true
fi
log "Rollback disarmed"
}
restore_previous_now() {
local previous="$1"
connection_exists "$previous" || fail "cannot restore previous connection: $previous"
log "Restoring previous connection now: $previous"
nmcli connection up "$previous" ifname "$INTERFACE"
}
cleanup_on_exit() {
local ec=$?
if ((ec != 0)); then
log "Script exit non-zero ($ec). Rollback remains armed if timer exists"
fi
}
trap cleanup_on_exit EXIT
main() {
require_root
require_cmd nmcli
require_cmd ping
require_cmd logger
[[ "$TARGET_SSID" != "CHANGE_ME_SSID" ]] || fail "set TARGET_SSID first"
[[ "$TARGET_PASSWORD" != "CHANGE_ME_PASSWORD" ]] || fail "set TARGET_PASSWORD first"
local previous
previous=$(current_connection)
connection_exists "$previous" || fail "no valid current connection to fallback to"
log "Current connection: $previous"
log "Target SSID: $TARGET_SSID"
[[ -n "$TARGET_BSSID" ]] && log "Target BSSID: $TARGET_BSSID"
nmcli device wifi rescan ifname "$INTERFACE" >/dev/null 2>&1 || true
start_rollback_timer "$previous"
local -a cmd=(nmcli device wifi connect "$TARGET_SSID" password "$TARGET_PASSWORD" ifname "$INTERFACE")
[[ -n "$TARGET_BSSID" ]] && cmd+=(bssid "$TARGET_BSSID")
log "Connecting to target Wi-Fi"
if ! "${cmd[@]}"; then
log "Target connect command failed"
restore_previous_now "$previous"
wait_for_health "$VERIFY_SECONDS" || fail "fallback attempted but health check failed"
disarm_rollback_timer
fail "restored previous connection after immediate connect failure"
fi
log "Connect command accepted. Verifying health"
if wait_for_health "$VERIFY_SECONDS"; then
log "Target Wi-Fi healthy"
disarm_rollback_timer
exit 0
fi
log "Target Wi-Fi not healthy in time. Immediate fallback"
restore_previous_now "$previous"
wait_for_health "$VERIFY_SECONDS" || fail "fallback attempted but health check failed"
disarm_rollback_timer
fail "restored previous connection after failed verification"
}
main "$@"
@fadlee

fadlee commented Apr 16, 2026

Copy link
Copy Markdown
Author

Cara pakai:

chmod +x switch-wifi.sh
sudo TARGET_SSID="NamaWifiBaru" TARGET_PASSWORD="PasswordWifi" ./switch-wifi.sh 

Kalau mau pakai BSSID spesifik:

sudo TARGET_SSID="NamaWifiBaru" TARGET_PASSWORD="PasswordWifi" TARGET_BSSID="AA:BB:CC:DD:EE:FF" ./switch-wifi.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment