diff --git a/d2ypp2/config/system-stack.env.example b/d2ypp2/config/system-stack.env.example index 8981056..2d54325 100644 --- a/d2ypp2/config/system-stack.env.example +++ b/d2ypp2/config/system-stack.env.example @@ -95,6 +95,8 @@ NEKO_WEBRTC_UDPMUX_1=52300 NEKO_WEBRTC_UDPMUX_2=52301 NEKO_WEBRTC_UDPMUX_3=52302 NEKO_WEBRTC_NAT1TO1= +# 后台 watcher 会定期核对当前 LAN IP;若板子切换 Wi‑Fi / DHCP 后 IP 变化,会自动重启 Neko 刷新 WebRTC NAT 地址 +NEKO_IP_WATCH_INTERVAL=15 NEKO_PLUGINS_ENABLED=true # Redroid Android 13(需要 binderfs 或 /dev/binder*;安装脚本会在支持的内核上自动准备) diff --git a/d2ypp2/scripts/linux/install_business.sh b/d2ypp2/scripts/linux/install_business.sh index deeb6ed..1aac145 100644 --- a/d2ypp2/scripts/linux/install_business.sh +++ b/d2ypp2/scripts/linux/install_business.sh @@ -30,6 +30,7 @@ configure_sudoers configure_mdns install_browser_stack install_neko_browser +install_neko_ip_watch_service install_redroid prepare_mitmproxy prepare_vscode_server diff --git a/d2ypp2/scripts/linux/install_hub.sh b/d2ypp2/scripts/linux/install_hub.sh index eaa2687..b07e764 100644 --- a/d2ypp2/scripts/linux/install_hub.sh +++ b/d2ypp2/scripts/linux/install_hub.sh @@ -29,6 +29,7 @@ configure_wifi_profile install_shellcrash install_browser_stack install_neko_browser +install_neko_ip_watch_service install_redroid prepare_mitmproxy prepare_vscode_server diff --git a/d2ypp2/scripts/linux/lib/common.sh b/d2ypp2/scripts/linux/lib/common.sh index 7f698df..ae03bc6 100644 --- a/d2ypp2/scripts/linux/lib/common.sh +++ b/d2ypp2/scripts/linux/lib/common.sh @@ -777,6 +777,36 @@ UNIT systemctl enable --now live-console.service } +install_neko_ip_watch_service() { + if [ "${ENABLE_NEKO:-1}" != "1" ]; then + systemctl disable --now live-neko-ip-watch.service >/dev/null 2>&1 || true + rm -f /etc/systemd/system/live-neko-ip-watch.service + systemctl daemon-reload + return 0 + fi + log "Installing Neko LAN IP watcher" + chmod +x "$ROOT_DIR/scripts/linux/neko_ip_watch.sh" + cat >/etc/systemd/system/live-neko-ip-watch.service </etc/sudoers.d/live-control-plane </dev/null | grep -qx "$name"; then + continue + fi + value="$( + docker inspect "$name" --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null | + awk -F= '/^NEKO_WEBRTC_NAT1TO1=/{print $2; exit}' + )" + if [ -n "$value" ]; then + printf '%s\n' "$value" + fi + done +} + +maybe_refresh_neko_for_ip_change() { + local preferred active interval cooldown active_value + + if [ "${ENABLE_NEKO:-1}" != "1" ] || ! have docker; then + return 0 + fi + if ! docker ps --format '{{.Names}}' 2>/dev/null | grep -Eq '^live-neko-[123]$'; then + return 0 + fi + + preferred="$(detect_preferred_lan_ip || true)" + if [ -z "$preferred" ]; then + return 0 + fi + + active="" + for active_value in $(running_neko_nat1to1_values || true); do + [ -n "$active_value" ] || continue + if [ "$active_value" != "$preferred" ]; then + active="$active_value" + break + fi + done + if [ -z "$active" ]; then + return 0 + fi + + interval="$(normalize_interval "${NEKO_IP_WATCH_INTERVAL:-15}")" + cooldown="$interval" + if [ "$cooldown" -lt 15 ]; then + cooldown=15 + fi + + log "Detected Neko LAN IP drift: container=${active} current=${preferred}; restarting Neko" + if bash "$SERVICE_CTL" neko restart; then + log "Neko restart finished; refreshed NAT1TO1=${preferred}" + else + log "WARN: Neko restart failed after LAN IP drift" + fi + sleep "$cooldown" +} + +main() { + load_env + reload_watch_env + log "Watching Neko LAN IP changes" + while true; do + reload_watch_env + maybe_refresh_neko_for_ip_change + sleep "$(normalize_interval "${NEKO_IP_WATCH_INTERVAL:-15}")" + done +} + +main "$@" diff --git a/d2ypp2/scripts/linux/refresh_live_console_unit.sh b/d2ypp2/scripts/linux/refresh_live_console_unit.sh index 4966113..0811c62 100644 --- a/d2ypp2/scripts/linux/refresh_live_console_unit.sh +++ b/d2ypp2/scripts/linux/refresh_live_console_unit.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# git pull 后若只改了 systemd 模板(如 StartLimit),执行本脚本重写 live-console.service 并 enable --now +# git pull 后若改了 systemd 模板(如 live-console 或 Neko watcher),执行本脚本重写对应 unit 并 enable --now set -euo pipefail SELF_DIR="$(cd "$(dirname "$0")" && pwd)" @@ -10,4 +10,6 @@ ensure_root "$@" load_env log "Rewriting /etc/systemd/system/live-console.service from repo" install_live_console_service +log "Rewriting /etc/systemd/system/live-neko-ip-watch.service from repo" +install_neko_ip_watch_service log "Done. Check: systemctl status live-console.service --no-pager" diff --git a/d2ypp2/scripts/linux/uninstall_stack.sh b/d2ypp2/scripts/linux/uninstall_stack.sh index f7e29e6..c28ce3b 100644 --- a/d2ypp2/scripts/linux/uninstall_stack.sh +++ b/d2ypp2/scripts/linux/uninstall_stack.sh @@ -9,7 +9,7 @@ ensure_root "$@" load_env log "Stopping systemd services" -for svc in live-edge.service live-console.service live-webtty.service live-android-panel.service live-pulseaudio.service live-kmscube.service live-redroid-host.service netdata cockpit.socket shellcrash.service avahi-daemon; do +for svc in live-edge.service live-console.service live-neko-ip-watch.service live-webtty.service live-android-panel.service live-pulseaudio.service live-kmscube.service live-redroid-host.service netdata cockpit.socket shellcrash.service avahi-daemon; do systemctl stop "$svc" >/dev/null 2>&1 || true systemctl disable "$svc" >/dev/null 2>&1 || true done @@ -42,6 +42,7 @@ fi log "Removing systemd unit files" rm -f /etc/systemd/system/live-edge.service rm -f /etc/systemd/system/live-console.service +rm -f /etc/systemd/system/live-neko-ip-watch.service rm -f /etc/systemd/system/live-webtty.service rm -f /etc/systemd/system/live-android-panel.service rm -f /etc/systemd/system/live-pulseaudio.service diff --git a/d2ypp2/scripts/linux/upgrade_live_console.sh b/d2ypp2/scripts/linux/upgrade_live_console.sh index 6972b90..96e29b9 100644 --- a/d2ypp2/scripts/linux/upgrade_live_console.sh +++ b/d2ypp2/scripts/linux/upgrade_live_console.sh @@ -20,6 +20,9 @@ prepare_python_runtime log "Rebuilding web-console (npm)" prepare_web_console +log "Refreshing Neko LAN IP watcher unit" +install_neko_ip_watch_service + log "Restarting live-console.service" systemctl daemon-reload systemctl restart live-console.service diff --git a/d2ypp2/tests/test_neko_config.py b/d2ypp2/tests/test_neko_config.py index a1a2185..66ae12b 100644 --- a/d2ypp2/tests/test_neko_config.py +++ b/d2ypp2/tests/test_neko_config.py @@ -86,6 +86,23 @@ class NekoConfigTests(unittest.TestCase): self.assertIn("NEKO_HTTP_PORT_2", common) self.assertIn("NEKO_WEBRTC_UDP_RANGE_3", common) + def test_neko_ip_watcher_is_installed_and_restarts_on_drift(self) -> None: + common = (ROOT / "scripts" / "linux" / "lib" / "common.sh").read_text(encoding="utf-8") + watcher = (ROOT / "scripts" / "linux" / "neko_ip_watch.sh").read_text(encoding="utf-8") + upgrade = (ROOT / "scripts" / "linux" / "upgrade_live_console.sh").read_text(encoding="utf-8") + uninstall = (ROOT / "scripts" / "linux" / "uninstall_stack.sh").read_text(encoding="utf-8") + stack_env = (ROOT / "config" / "system-stack.env.example").read_text(encoding="utf-8") + self.assertIn("install_neko_ip_watch_service()", common) + self.assertIn("live-neko-ip-watch.service", common) + self.assertIn("ExecStart=/bin/bash", common) + self.assertIn("scripts/linux/neko_ip_watch.sh", common) + self.assertIn("detect_preferred_lan_ip", watcher) + self.assertIn('bash "$SERVICE_CTL" neko restart', watcher) + self.assertIn("NEKO_IP_WATCH_INTERVAL", watcher) + self.assertIn("install_neko_ip_watch_service", upgrade) + self.assertIn("live-neko-ip-watch.service", uninstall) + self.assertIn("NEKO_IP_WATCH_INTERVAL=15", stack_env) + def test_local_browser_prefers_chrome_on_x86_and_chromium_elsewhere(self) -> None: script = (ROOT / "scripts" / "linux" / "launch_browser.sh").read_text(encoding="utf-8") self.assertIn("x86_64|amd64", script)