diff --git a/selective-vpn-gui/traffic_mode_dialog.py b/selective-vpn-gui/traffic_mode_dialog.py index 49f81ce..5eb0a6b 100644 --- a/selective-vpn-gui/traffic_mode_dialog.py +++ b/selective-vpn-gui/traffic_mode_dialog.py @@ -917,13 +917,27 @@ RU: Применяет policy-rules и проверяет health. При оши "systemd-run", "--user", "--scope", + "--no-block", "--unit", unit, "--collect", "--same-dir", ] + args - p = subprocess.run(run_cmd, capture_output=True, text=True, check=False) + try: + p = subprocess.run( + run_cmd, + capture_output=True, + text=True, + check=False, + timeout=6, + ) + except subprocess.TimeoutExpired as e: + raise RuntimeError( + "systemd-run timed out (UI would freeze without this guard). " + "Try again or verify that systemd --user is responsive.\n\n" + f"command: {' '.join(run_cmd)}" + ) from e out = ((p.stdout or "") + (p.stderr or "")).strip() if p.returncode != 0: raise RuntimeError(f"systemd-run failed: {p.returncode}\n{out}".strip()) @@ -1154,14 +1168,19 @@ RU: Применяет policy-rules и проверяет health. При оши return "" def _systemctl_user(self, args: list[str]) -> tuple[int, str]: - p = subprocess.run( - ["systemctl", "--user"] + list(args or []), - capture_output=True, - text=True, - check=False, - ) - out = ((p.stdout or "") + (p.stderr or "")).strip() - return int(p.returncode or 0), out + cmd = ["systemctl", "--user"] + list(args or []) + try: + p = subprocess.run( + cmd, + capture_output=True, + text=True, + check=False, + timeout=4, + ) + out = ((p.stdout or "") + (p.stderr or "")).strip() + return int(p.returncode or 0), out + except subprocess.TimeoutExpired: + return 124, f"timeout running: {' '.join(cmd)}" def _list_running_svpn_scopes(self) -> list[str]: code, out = self._systemctl_user(