ui: appmarks use effective cgroup via MainPID
This commit is contained in:
@@ -940,14 +940,77 @@ RU: Применяет policy-rules и проверяет health. При оши
|
|||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise RuntimeError(f"systemd-run failed: {p.returncode}\n{out}".strip())
|
raise RuntimeError(f"systemd-run failed: {p.returncode}\n{out}".strip())
|
||||||
|
|
||||||
# EN: Some apps (e.g. Chrome wrappers) can return quickly; the transient scope
|
# EN: Some apps can be migrated into a different app scope by the desktop/session
|
||||||
# EN: may appear/disappear fast. Retry briefly to avoid race.
|
# EN: integration. Using unit ControlGroup is then incorrect. Prefer reading the
|
||||||
# RU: Некоторые приложения (например, chrome-wrapper) быстро завершаются; scope
|
# EN: effective cgroup from the unit MainPID (/proc/<pid>/cgroup) and fall back
|
||||||
# RU: может появиться/исчезнуть очень быстро. Делаем небольшой retry.
|
# EN: to systemctl ControlGroup only if needed.
|
||||||
cg = self._control_group_for_unit_retry(unit, timeout_sec=3.0)
|
# RU: Некоторые приложения могут мигрировать в другой app scope (интеграция
|
||||||
|
# RU: с desktop/session). Тогда ControlGroup юнита неверен. Предпочитаем читать
|
||||||
|
# RU: реальный cgroup по MainPID (/proc/<pid>/cgroup) и только потом fallback
|
||||||
|
# RU: на systemctl ControlGroup.
|
||||||
|
cg = self._effective_cgroup_for_unit_retry(unit, timeout_sec=3.0)
|
||||||
|
|
||||||
return cg, out
|
return cg, out
|
||||||
|
|
||||||
|
def _effective_cgroup_for_unit_retry(self, unit: str, *, timeout_sec: float = 2.0) -> str:
|
||||||
|
u = (unit or "").strip()
|
||||||
|
if not u:
|
||||||
|
raise ValueError("empty unit")
|
||||||
|
|
||||||
|
deadline = time.time() + max(0.2, float(timeout_sec or 0))
|
||||||
|
last_pid_out = ""
|
||||||
|
while time.time() < deadline:
|
||||||
|
code, out = self._systemctl_user(["show", "-p", "MainPID", "--value", u])
|
||||||
|
last_pid_out = out or ""
|
||||||
|
if code == 0:
|
||||||
|
try:
|
||||||
|
pid = int((out or "").strip() or "0")
|
||||||
|
except Exception:
|
||||||
|
pid = 0
|
||||||
|
if pid > 0:
|
||||||
|
cg = self._cgroup_path_from_pid(pid)
|
||||||
|
if cg:
|
||||||
|
return cg
|
||||||
|
low = (out or "").lower()
|
||||||
|
if "could not be found" in low or "not found" in low:
|
||||||
|
break
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# Fallback: unit ControlGroup (may be wrong for migrated apps).
|
||||||
|
try:
|
||||||
|
cg = self._control_group_for_unit_retry(u, timeout_sec=1.0)
|
||||||
|
if cg:
|
||||||
|
return cg
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
(
|
||||||
|
"failed to query effective cgroup\n"
|
||||||
|
+ (last_pid_out.strip() or "(no output)")
|
||||||
|
+ "\n\n"
|
||||||
|
+ "EN: Could not resolve unit MainPID->/proc/<pid>/cgroup.\n"
|
||||||
|
+ "RU: Не удалось получить MainPID и прочитать /proc/<pid>/cgroup."
|
||||||
|
).strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
def _cgroup_path_from_pid(self, pid: int) -> str:
|
||||||
|
p = int(pid or 0)
|
||||||
|
if p <= 0:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
with open(f"/proc/{p}/cgroup", "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
for raw in f:
|
||||||
|
line = (raw or "").strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
if line.startswith("0::"):
|
||||||
|
cg = line[len("0::") :].strip()
|
||||||
|
return cg
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
return ""
|
||||||
|
|
||||||
def _control_group_for_unit_retry(self, unit: str, *, timeout_sec: float = 2.0) -> str:
|
def _control_group_for_unit_retry(self, unit: str, *, timeout_sec: float = 2.0) -> str:
|
||||||
u = (unit or "").strip()
|
u = (unit or "").strip()
|
||||||
if not u:
|
if not u:
|
||||||
@@ -1239,7 +1302,20 @@ RU: Применяет policy-rules и проверяет health. При оши
|
|||||||
cg = ""
|
cg = ""
|
||||||
cg_id = 0
|
cg_id = 0
|
||||||
try:
|
try:
|
||||||
cg = self._control_group_for_unit(unit)
|
# Prefer effective cgroup via MainPID (/proc/<pid>/cgroup) for services.
|
||||||
|
# Some GUI apps can migrate into a different app scope after launch.
|
||||||
|
if unit.endswith(".service"):
|
||||||
|
code, out = self._systemctl_user(["show", "-p", "MainPID", "--value", unit])
|
||||||
|
if code == 0:
|
||||||
|
try:
|
||||||
|
pid = int((out or "").strip() or "0")
|
||||||
|
except Exception:
|
||||||
|
pid = 0
|
||||||
|
if pid > 0:
|
||||||
|
cg = self._cgroup_path_from_pid(pid)
|
||||||
|
|
||||||
|
if not cg:
|
||||||
|
cg = self._control_group_for_unit(unit)
|
||||||
cg_id = self._cgroup_inode_id(cg)
|
cg_id = self._cgroup_inode_id(cg)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user