#!/usr/bin/env bash # # bt-netconfig-boot - re-apply saved network settings after reboot. # Runs as root from buttontask-network.service once NetworkManager is up. # Does not block UI startup: short waits, bounded activation, best-effort apply. set -uo pipefail ROOT="${BUTTONTASK_ROOT:-/opt/buttontask}" CFG="$ROOT/config/network.json" SCRIPT="$ROOT/scripts/bt-netconfig" NM_WAIT_SEC="${BUTTONTASK_NET_NM_WAIT:-15}" IFACE_WAIT_SEC="${BUTTONTASK_NET_IFACE_WAIT:-10}" ACTIVATE_TIMEOUT="${BUTTONTASK_NET_ACTIVATE_TIMEOUT:-10}" log() { echo "bt-netconfig-boot: $*"; } wait_for_iface() { local iface="$1" i for i in $(seq 1 "$IFACE_WAIT_SEC"); do ip link show "$iface" &>/dev/null && return 0 sleep 1 done return 1 } wait_for_nm() { local i for i in $(seq 1 "$NM_WAIT_SEC"); do nmcli general status &>/dev/null && return 0 sleep 1 done return 1 } activate_profile() { local con="$1" iface="$2" if command -v timeout >/dev/null 2>&1; then timeout "$ACTIVATE_TIMEOUT" nmcli connection up "$con" ifname "$iface" 2>/dev/null \ || timeout "$ACTIVATE_TIMEOUT" nmcli connection up "$con" 2>/dev/null \ || true else nmcli connection up "$con" ifname "$iface" 2>/dev/null \ || nmcli connection up "$con" 2>/dev/null || true fi } apply_saved() { [ -f "$CFG" ] || { log "no saved config at $CFG"; return 0; } [ -x "$SCRIPT" ] || { log "bt-netconfig missing: $SCRIPT"; return 1; } BUTTONTASK_NET_BOOT=1 \ BUTTONTASK_NET_ACTIVATE_TIMEOUT="$ACTIVATE_TIMEOUT" \ python3 - "$CFG" "$SCRIPT" <<'PY' import json, os, subprocess, sys cfg_path, script = sys.argv[1], sys.argv[2] with open(cfg_path, encoding="utf-8") as f: cfg = json.load(f) iface = (cfg.get("iface") or "").strip() mode = (cfg.get("mode") or "").strip() if not iface or mode not in ("dhcp", "static"): sys.exit(0) args = [script, iface, mode] if mode == "static": args += [ cfg.get("address", ""), str(cfg.get("prefix", 24)), cfg.get("gateway", ""), cfg.get("dns", ""), ] env = os.environ.copy() env.setdefault("BUTTONTASK_NET_BOOT", "1") raise SystemExit(subprocess.run(args, env=env).returncode) PY } IFACE="eth0" if [ -f "$CFG" ]; then IFACE="$(python3 -c "import json; print(json.load(open('$CFG')).get('iface','eth0'))" 2>/dev/null || echo eth0)" fi wait_for_nm || log "NetworkManager not ready, continuing anyway" wait_for_iface "$IFACE" || log "interface $IFACE not found, continuing anyway" if apply_saved; then log "network profile applied on $IFACE (activation may continue in background)" else log "saved config apply failed, trying buttontask profiles" while IFS= read -r con; do [ -z "$con" ] && continue iface="${con#buttontask-}" activate_profile "$con" "$iface" done < <(nmcli -t -f NAME connection show 2>/dev/null | grep '^buttontask-' || true) fi