From bf1c6761fd30ccc84dcb37ed2d7534607474dd2a Mon Sep 17 00:00:00 2001 From: orohi Date: Mon, 3 Aug 2026 12:18:53 +0300 Subject: [PATCH] Fix mita RPC socket path and rate-limit heal recovery. --- app.py | 2 +- managers/mieru_manager.py | 79 ++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/app.py b/app.py index e7ecbf4..ee42cf1 100644 --- a/app.py +++ b/app.py @@ -103,7 +103,7 @@ else: application_path = os.path.dirname(__file__) DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export -CURRENT_VERSION = "v2.6.8" +CURRENT_VERSION = "v2.6.9" RELEASES_REPO_URL = repo_url() RELEASES_API_LATEST = api_latest_url() BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin')) diff --git a/managers/mieru_manager.py b/managers/mieru_manager.py index 76d5248..a1c8f94 100644 --- a/managers/mieru_manager.py +++ b/managers/mieru_manager.py @@ -21,7 +21,9 @@ logger = logging.getLogger(__name__) MIERU_RELEASE = '3.28.0' GITHUB_RELEASE = f'https://github.com/enfein/mieru/releases/download/v{MIERU_RELEASE}' -MITA_SOCK = '/var/run/mita.sock' +# Official mita UDS (v3.x). Older docs sometimes mention /var/run/mita.sock. +MITA_SOCK = '/var/run/mita/mita.sock' +MITA_SOCK_LEGACY = '/var/run/mita.sock' # Official package persists applied config here. If this file has portBindings # but no users, `mita run` (systemd) auto-starts the proxy and FATAL-exits # with "socks5 server listening failed: no user found", crashing the daemon. @@ -246,10 +248,14 @@ class MieruManager: self.ssh.run_sudo_command( f"systemctl stop {self.SERVICE_NAME} 2>/dev/null || true; " f"systemctl reset-failed {self.SERVICE_NAME} 2>/dev/null || true; " - f"rm -f {_q(MITA_SOCK)} /var/run/mita/*.sock " - f"{_q(MITA_CONFIG_PB)} {_q(MITA_CONFIG_JSON)} 2>/dev/null || true", + f"rm -f {_q(MITA_SOCK)} {_q(MITA_SOCK_LEGACY)} /var/run/mita/*.sock " + f"{_q(MITA_CONFIG_PB)} {_q(MITA_CONFIG_JSON)} 2>/dev/null || true; " + f"mkdir -p /var/run/mita /etc/mita 2>/dev/null || true", timeout=60, ) + # systemd StartLimitBurst: wait out "Start request repeated too quickly". + time.sleep(6) + clients = self._ensure_bootstrap_clients(self._read_clients()) self._write_clients(clients) meta = self._read_metadata() @@ -258,18 +264,27 @@ class MieruManager: self._write_file(self.config_path, json.dumps(config, indent=2)) self.ssh.run_sudo_command( + f"systemctl reset-failed {self.SERVICE_NAME} 2>/dev/null || true; " f"systemctl start {self.SERVICE_NAME}", timeout=60, ) - if not self._wait_for_rpc(timeout=45): - journal, _, _ = self.ssh.run_sudo_command( - f"journalctl -u {self.SERVICE_NAME} -n 40 --no-pager 2>&1", - timeout=30, - ) - raise RuntimeError( - 'mita daemon still not ready after heal. ' - f'journal: {(journal or "").strip()[-500:]}' + if not self._wait_for_rpc(timeout=60): + # Second attempt after another rate-limit window. + self.ssh.run_sudo_command( + f"systemctl reset-failed {self.SERVICE_NAME} 2>/dev/null || true; " + f"systemctl restart {self.SERVICE_NAME}", + timeout=60, ) + time.sleep(3) + if not self._wait_for_rpc(timeout=45): + journal, _, _ = self.ssh.run_sudo_command( + f"journalctl -u {self.SERVICE_NAME} -n 50 --no-pager 2>&1", + timeout=30, + ) + raise RuntimeError( + 'mita daemon still not ready after heal. ' + f'journal: {(journal or "").strip()[-600:]}' + ) out, err, code = self._mita_cli( ['apply', 'config', _q(self.config_path)], timeout=60, @@ -284,7 +299,8 @@ class MieruManager: def _ensure_daemon(self, log=None): """Ensure mita systemd unit is up and RPC socket answers.""" self.ssh.run_sudo_command( - f"systemctl enable {self.SERVICE_NAME} 2>/dev/null || true", + f"systemctl enable {self.SERVICE_NAME} 2>/dev/null || true; " + f"mkdir -p /var/run/mita /etc/mita 2>/dev/null || true", timeout=30, ) # Official package expects the operating user in group `mita`. @@ -308,37 +324,30 @@ class MieruManager: timeout=60, ) if not self._wait_for_rpc(timeout=45): - # Crash loop / stale socket — wipe broken store and recover. - if self._daemon_needs_heal(): - self._heal_mita_store(log) - else: - self.ssh.run_sudo_command( - f"systemctl stop {self.SERVICE_NAME} 2>/dev/null || true; " - f"rm -f {_q(MITA_SOCK)} /var/run/mita/*.sock 2>/dev/null || true; " - f"systemctl start {self.SERVICE_NAME}", - timeout=60, - ) - if not self._wait_for_rpc(timeout=45): - # Last resort: heal even if journal didn't match yet. - self._heal_mita_store(log) + # Crash loop / wrong socket / rate-limit — wipe store and recover. + self._heal_mita_store(log) if log is not None: log.append('mita daemon is active') def _wait_for_rpc(self, timeout=30): deadline = time.time() + timeout + sock_check = ( + f"(test -S {_q(MITA_SOCK)} || test -S {_q(MITA_SOCK_LEGACY)}) && echo ok" + ) while time.time() < deadline: - sock_out, _, sock_code = self.ssh.run_sudo_command( - f"test -S {_q(MITA_SOCK)} && echo ok" + # Prefer CLI status: if it answers IDLE/RUNNING, RPC is up + # regardless of which sock path we expected. + status_out, _, status_code = self.ssh.run_sudo_command( + 'mita status 2>&1', + timeout=20, ) + text = (status_out or '').upper() + if status_code == 0 and ('IDLE' in text or 'RUNNING' in text): + return True + sock_out, _, sock_code = self.ssh.run_sudo_command(sock_check) if sock_code == 0 and 'ok' in (sock_out or ''): - status_out, _, status_code = self.ssh.run_sudo_command( - 'mita status 2>&1', - timeout=20, - ) - text = (status_out or '').upper() - if status_code == 0 and ('IDLE' in text or 'RUNNING' in text): - return True - # Socket exists but CLI still races — brief pause. + time.sleep(1) + continue time.sleep(1.5) return False