refactor(ui): reuse _launch_and_mark for Run tab

This commit is contained in:
beckline
2026-02-16 00:16:08 +03:00
parent b39ff5f0d9
commit a7a6091b78

View File

@@ -1147,7 +1147,7 @@ RU: Применяет policy-rules и проверяет health. При оши
if code == 0 and (out or "").strip().lower() == "active": if code == 0 and (out or "").strip().lower() == "active":
cg = self._effective_cgroup_for_unit_retry(unit, timeout_sec=3.0) cg = self._effective_cgroup_for_unit_retry(unit, timeout_sec=3.0)
self._append_app_log( 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( res = self.ctrl.traffic_appmarks_apply(
op="add", op="add",
@@ -1174,14 +1174,15 @@ RU: Применяет policy-rules и проверяет health. При оши
self.refresh_appmarks_items(quiet=True) self.refresh_appmarks_items(quiet=True)
self.refresh_appmarks_counts() self.refresh_appmarks_counts()
self.refresh_running_scopes(quiet=True) self.refresh_running_scopes(quiet=True)
self.refresh_app_profiles(quiet=True)
return return
unit = f"svpn-{tgt}-{int(time.time())}.service" 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) cg, out = self._run_systemd_unit(cmdline, unit=unit)
if out: if out:
self._append_app_log(f"[profile] systemd-run:\n{out}") self._append_app_log(f"[app] systemd-run:\n{out}")
self._append_app_log(f"[profile] ControlGroup: {cg}") self._append_app_log(f"[app] ControlGroup: {cg}")
self._set_last_scope(unit=unit, target=tgt, app_key=key, cmdline=cmdline, cgroup_id=0) self._set_last_scope(unit=unit, target=tgt, app_key=key, cmdline=cmdline, cgroup_id=0)
res = self.ctrl.traffic_appmarks_apply( res = self.ctrl.traffic_appmarks_apply(
@@ -1194,6 +1195,16 @@ RU: Применяет policy-rules и проверяет health. При оши
timeout_sec=ttl, timeout_sec=ttl,
) )
if not res.ok: 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") 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") 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_items(quiet=True)
self.refresh_appmarks_counts() self.refresh_appmarks_counts()
self.refresh_running_scopes(quiet=True) self.refresh_running_scopes(quiet=True)
self.refresh_app_profiles(quiet=True)
def _selected_app_profile(self): def _selected_app_profile(self):
it = self.lst_app_profiles.currentItem() 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.") QMessageBox.warning(self, "Missing command", "Please enter a command to run.")
return 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" target = "vpn" if self.rad_app_vpn.isChecked() else "direct"
ttl_sec = int(self.spn_app_ttl.value()) * 3600 ttl_sec = int(self.spn_app_ttl.value()) * 3600
app_key = self._infer_app_key_from_cmdline(cmdline)
# EN: If the app is already running inside the last svpn unit, don't spawn a new instance. self._launch_and_mark(cmdline=cmdline, target=target, ttl_sec=ttl_sec, app_key=app_key)
# 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)
self._safe(work, title="Run app error") self._safe(work, title="Run app error")