#!/usr/bin/env bash # Push a single config file to the router with an automatic revert safety net. # # Usage: ./safe-apply.sh [revert-minutes] # config-name: e.g. "network", "wireless", "firewall" # revert-minutes: how long before auto-revert fires (default: 5) # # The router will automatically reboot (and revert to its saved config) after # REVERT_MINS minutes unless you explicitly confirm the change is working. # On confirmation, the pending reboot is cancelled and the config is committed. set -euo pipefail CONFIG_NAME="${1:-}" REVERT_MINS="${2:-5}" ROUTER="${ROUTER:-openwrt}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_FILE="$SCRIPT_DIR/../config/$CONFIG_NAME" if [[ -z "$CONFIG_NAME" ]]; then echo "Usage: $0 [revert-minutes]" exit 1 fi if [[ ! -f "$CONFIG_FILE" ]]; then echo "Error: $CONFIG_FILE not found." exit 1 fi echo "==> Staging auto-revert in ${REVERT_MINS} minutes on router..." ssh "$ROUTER" "echo 'reboot' | at now + ${REVERT_MINS} minutes 2>/dev/null || { sleep ${REVERT_MINS}m && reboot; } &" echo "==> Pushing $CONFIG_NAME..." ssh "$ROUTER" "cat > /etc/config/$CONFIG_NAME" < "$CONFIG_FILE" echo "==> Reloading service..." case "$CONFIG_NAME" in network) ssh "$ROUTER" "/etc/init.d/network restart" ;; wireless) ssh "$ROUTER" "/etc/init.d/network restart" ;; firewall) ssh "$ROUTER" "/etc/init.d/firewall restart" ;; dhcp) ssh "$ROUTER" "/etc/init.d/dnsmasq restart" ;; *) ssh "$ROUTER" "uci commit && reload_config" ;; esac echo "" echo "Config applied. You have ${REVERT_MINS} minutes to confirm." echo "Test your connection, then come back here." echo "" read -rp "Is everything working? Confirm to cancel revert [y/N] " answer if [[ "${answer,,}" == "y" ]]; then ssh "$ROUTER" "kill \$(atq 2>/dev/null | awk '{print \$1}' | xargs -I{} at -l {} 2>/dev/null | grep -l reboot | xargs) 2>/dev/null; killall -q sleep 2>/dev/null || true" ssh "$ROUTER" "uci commit" echo "Confirmed. Revert cancelled, config committed on router." else echo "Reverting — router will reboot in remaining window." fi