diff --git a/selective-vpn-gui/traffic_mode_dialog.py b/selective-vpn-gui/traffic_mode_dialog.py index 45b06b6..28a3416 100644 --- a/selective-vpn-gui/traffic_mode_dialog.py +++ b/selective-vpn-gui/traffic_mode_dialog.py @@ -1147,7 +1147,7 @@ RU: Применяет policy-rules и проверяет health. При оши if code == 0 and (out or "").strip().lower() == "active": cg = self._effective_cgroup_for_unit_retry(unit, timeout_sec=3.0) self._append_app_log( - f"[profile] already running: app={key} target={tgt} unit={unit} (refreshing mark)" + f"[app] already running: app={key} target={tgt} unit={unit} (refreshing mark)" ) res = self.ctrl.traffic_appmarks_apply( op="add", @@ -1174,14 +1174,15 @@ RU: Применяет policy-rules и проверяет health. При оши self.refresh_appmarks_items(quiet=True) self.refresh_appmarks_counts() self.refresh_running_scopes(quiet=True) + self.refresh_app_profiles(quiet=True) return unit = f"svpn-{tgt}-{int(time.time())}.service" - self._append_app_log(f"[profile] launching: app={key or '-'} target={tgt} ttl={ttl}s unit={unit}") + self._append_app_log(f"[app] launching: app={key or '-'} target={tgt} ttl={ttl}s unit={unit}") cg, out = self._run_systemd_unit(cmdline, unit=unit) if out: - self._append_app_log(f"[profile] systemd-run:\n{out}") - self._append_app_log(f"[profile] ControlGroup: {cg}") + self._append_app_log(f"[app] systemd-run:\n{out}") + self._append_app_log(f"[app] ControlGroup: {cg}") self._set_last_scope(unit=unit, target=tgt, app_key=key, cmdline=cmdline, cgroup_id=0) res = self.ctrl.traffic_appmarks_apply( @@ -1194,6 +1195,16 @@ RU: Применяет policy-rules и проверяет health. При оши timeout_sec=ttl, ) if not res.ok: + low = (res.message or "").lower() + if "cgroupv2 path fails" in low or "no such file or directory" in low: + raise RuntimeError( + (res.message or "appmark apply failed") + + "\n\n" + + "EN: This usually means the app didn't stay inside the new systemd unit " + + "(often because it was already running). Close the app completely and run again.\n" + + "RU: Обычно это значит, что приложение не осталось в новом systemd unit " + + "(часто потому что оно уже было запущено). Полностью закрой приложение и запусти снова." + ) raise RuntimeError(res.message or "appmark apply failed") self._append_app_log(f"[appmarks] OK: {res.message} cgroup_id={res.cgroup_id} timeout={res.timeout_sec}s") @@ -1208,6 +1219,7 @@ RU: Применяет policy-rules и проверяет health. При оши self.refresh_appmarks_items(quiet=True) self.refresh_appmarks_counts() self.refresh_running_scopes(quiet=True) + self.refresh_app_profiles(quiet=True) def _selected_app_profile(self): it = self.lst_app_profiles.currentItem() @@ -1972,132 +1984,10 @@ RU: Применяет policy-rules и проверяет health. При оши QMessageBox.warning(self, "Missing command", "Please enter a command to run.") return - app_key = "" - try: - args = shlex.split(cmdline or "") - if args: - app_key = str(args[0] or "").strip() - except Exception: - app_key = "" - if not app_key: - # Fallback: best-effort first token. - app_key = (cmdline.split() or [""])[0].strip() - target = "vpn" if self.rad_app_vpn.isChecked() else "direct" ttl_sec = int(self.spn_app_ttl.value()) * 3600 - - # EN: If the app is already running inside the last svpn unit, don't spawn a new instance. - # RU: Если приложение уже запущено в последнем svpn unit, не запускаем второй экземпляр. - last_unit = (self._last_app_unit or "").strip() - last_target = (self._last_app_target or "").strip().lower() - last_key = (self._last_app_key or "").strip() - if last_unit and last_target == target and last_key and last_key == app_key: - code, out = self._systemctl_user(["is-active", last_unit]) - if code == 0 and (out or "").strip().lower() == "active": - cg = self._effective_cgroup_for_unit_retry(last_unit, timeout_sec=3.0) - self._append_app_log( - f"[app] already running: app={app_key} target={target} unit={last_unit} (refreshing mark)" - ) - res = self.ctrl.traffic_appmarks_apply( - op="add", - target=target, - cgroup=cg, - unit=last_unit, - command=cmdline, - app_key=app_key, - timeout_sec=ttl_sec, - ) - if res.ok: - self._append_app_log( - f"[appmarks] OK: {res.message} cgroup_id={res.cgroup_id} timeout={res.timeout_sec}s" - ) - self._set_action_status( - f"App mark refreshed: target={target} cgroup_id={res.cgroup_id}", - ok=True, - ) - self._set_last_scope( - unit=last_unit, - target=target, - app_key=app_key, - cmdline=cmdline, - cgroup_id=int(res.cgroup_id or 0), - ) - else: - self._append_app_log(f"[appmarks] ERROR: {res.message}") - self._set_action_status( - f"App mark refresh failed: target={target} ({res.message})", - ok=False, - ) - QMessageBox.critical(self, "App mark error", res.message or "unknown error") - self.refresh_appmarks_counts() - self.refresh_running_scopes() - self.refresh_app_profiles(quiet=True) - return - - unit = f"svpn-{target}-{int(time.time())}.service" - - self._append_app_log( - f"[app] launching: app={app_key or '-'} target={target} ttl={ttl_sec}s unit={unit}" - ) - - cg, out = self._run_systemd_unit(cmdline, unit=unit) - if out: - self._append_app_log(f"[app] systemd-run:\n{out}") - self._append_app_log(f"[app] ControlGroup: {cg}") - self._set_last_scope(unit=unit, target=target, app_key=app_key, cmdline=cmdline, cgroup_id=0) - - res = self.ctrl.traffic_appmarks_apply( - op="add", - target=target, - cgroup=cg, - unit=unit, - command=cmdline, - app_key=app_key, - timeout_sec=ttl_sec, - ) - if res.ok: - self._append_app_log( - f"[appmarks] OK: {res.message} cgroup_id={res.cgroup_id} timeout={res.timeout_sec}s" - ) - self._set_action_status( - f"App mark added: target={target} cgroup_id={res.cgroup_id}", - ok=True, - ) - self._set_last_scope( - unit=unit, - target=target, - app_key=app_key, - cmdline=cmdline, - cgroup_id=int(res.cgroup_id or 0), - ) - else: - self._append_app_log(f"[appmarks] ERROR: {res.message}") - self._set_action_status( - f"App mark failed: target={target} ({res.message})", - ok=False, - ) - low = (res.message or "").lower() - if "cgroupv2 path fails" in low or "no such file or directory" in low: - QMessageBox.critical( - self, - "App mark error", - (res.message or "unknown error") - + "\n\n" - + "EN: This usually means the app didn't stay inside the new systemd unit " - + "(often because it was already running). Close the app completely and run again from here.\n" - + "RU: Обычно это значит, что приложение не осталось в новом systemd unit " - + "(часто потому что оно уже было запущено). Полностью закрой приложение и запусти снова отсюда.", - ) - else: - QMessageBox.critical( - self, - "App mark error", - res.message or "unknown error", - ) - - self.refresh_appmarks_counts() - self.refresh_running_scopes() - self.refresh_app_profiles(quiet=True) + app_key = self._infer_app_key_from_cmdline(cmdline) + self._launch_and_mark(cmdline=cmdline, target=target, ttl_sec=ttl_sec, app_key=app_key) self._safe(work, title="Run app error")