From 7200ad179c18a848656be0ecc8e384ef4d73ed89 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Sat, 14 Feb 2026 16:45:37 +0100 Subject: [PATCH] Harden systemd scope cleanup Tighten transient unit lifecycle handling in GameCenter. - Drop stale unit watcher mappings on PropertiesChanged for removed sessions. - When stopping a monitored launch due to timeout or attach failure, fall back to terminating scope PIDs if StopUnit fails unexpectedly. This reduces leaked scopes and improves robustness in failure paths. --- src/gamecenter/gamecenterdaemon.cpp | 57 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/gamecenter/gamecenterdaemon.cpp b/src/gamecenter/gamecenterdaemon.cpp index b3678dd..5f688d5 100644 --- a/src/gamecenter/gamecenterdaemon.cpp +++ b/src/gamecenter/gamecenterdaemon.cpp @@ -833,6 +833,15 @@ void GameCenterDaemon::handleSystemdUnitPropertiesChanged(const QDBusObjectPath auto sit = m_sessions.find(sessionId); if (sit == m_sessions.end()) { + unwatchSystemdUnit({}, unitPath); + + for (auto nameIt = m_unitNameToSessionId.begin(); nameIt != m_unitNameToSessionId.end();) { + if (nameIt.value() == sessionId) { + nameIt = m_unitNameToSessionId.erase(nameIt); + } else { + ++nameIt; + } + } return; } @@ -1570,8 +1579,31 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q QVariantMap finalState = sessionToVariantMap(it.value(), stopping ? QStringLiteral("Stopped") : QStringLiteral("Failed")); if (!it.value().unitName.isEmpty()) { - m_systemd.stopUnit(it.value().unitName); - unwatchSystemdUnit(it.value().unitName, it.value().unitPath); + const QString unitName = it.value().unitName; + const QDBusObjectPath unitPath = it.value().unitPath; + const uint mainPid = it.value().mainPid; + + const QDBusReply stopReply = m_systemd.stopUnit(unitName); + if (!stopReply.isValid() && stopReply.error().name() != QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) { + QList pids; + if (!unitPath.path().isEmpty()) { + pids = m_systemd.scopePids(unitPath); + } + if (pids.isEmpty() && it.value().process && it.value().process->processId() > 0) { + pids = {static_cast(it.value().process->processId())}; + } + if (pids.isEmpty() && mainPid > 0) { + pids = {mainPid}; + } + if (!pids.isEmpty()) { + terminatePids(pids); + QTimer::singleShot(5000, this, [pids]() { + killPids(pids); + }); + } + } + + unwatchSystemdUnit(unitName, unitPath); } if (it.value().scanner) { @@ -1643,8 +1675,25 @@ void GameCenterDaemon::attachPidsToSession(const QString &sessionId, const QList }; if (!it.value().unitName.isEmpty()) { - m_systemd.stopUnit(it.value().unitName); - unwatchSystemdUnit(it.value().unitName, it.value().unitPath); + const QString staleUnitName = it.value().unitName; + const QDBusObjectPath staleUnitPath = it.value().unitPath; + + const QDBusReply stopReply = m_systemd.stopUnit(staleUnitName); + if (!stopReply.isValid() && stopReply.error().name() != QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) { + QList stalePids; + if (!staleUnitPath.path().isEmpty()) { + stalePids = m_systemd.scopePids(staleUnitPath); + } + if (stalePids.isEmpty()) { + stalePids = pids; + } + terminatePids(stalePids); + QTimer::singleShot(5000, this, [stalePids]() { + killPids(stalePids); + }); + } + + unwatchSystemdUnit(staleUnitName, staleUnitPath); } QVariantMap finalState = sessionToVariantMap(it.value(), QStringLiteral("Failed"));