mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-27 01:03:09 +00:00
Harden transient scope lifecycle
Improve GameCenter daemon handling of systemd transient scopes. - Watch monitored-launch scopes early and avoid dropping Launching sessions when the unit disappears before PIDs are attached. - Stop leaked scopes on AttachProcessesToUnit failures. - Make Stop() resilient for recovered sessions by falling back to TERM/KILL on scope PIDs when StopUnit fails unexpectedly. - Only recover scopes that match the expected A-La-Karte description.
This commit is contained in:
parent
d56b91dbd0
commit
985f6dac03
1 changed files with 70 additions and 1 deletions
|
|
@ -803,6 +803,18 @@ void GameCenterDaemon::handleSystemdUnitRemoved(const QString &unitName, const Q
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sit.value().scanner) {
|
||||||
|
if (!sit.value().unitPath.path().isEmpty()) {
|
||||||
|
const QString unitPathKey = sit.value().unitPath.path();
|
||||||
|
m_unitPathToSessionId.remove(unitPathKey);
|
||||||
|
if (QObject *watcher = m_unitPathWatchers.take(unitPathKey)) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sit.value().unitPath = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unwatchSystemdUnit(sit.value().unitName, sit.value().unitPath);
|
unwatchSystemdUnit(sit.value().unitName, sit.value().unitPath);
|
||||||
removeSessionInternal(sessionId, sit.value().stopping ? QStringLiteral("Stopped") : QStringLiteral("Exited"));
|
removeSessionInternal(sessionId, sit.value().stopping ? QStringLiteral("Stopped") : QStringLiteral("Exited"));
|
||||||
}
|
}
|
||||||
|
|
@ -833,6 +845,25 @@ void GameCenterDaemon::handleSystemdUnitPropertiesChanged(const QDBusObjectPath
|
||||||
}
|
}
|
||||||
const QString activeState = activeStateV.toString();
|
const QString activeState = activeStateV.toString();
|
||||||
|
|
||||||
|
if (sit.value().scanner) {
|
||||||
|
if (activeState == QLatin1String("active") || activeState == QLatin1String("activating") || activeState == QLatin1String("deactivating")) {
|
||||||
|
const QList<uint> pids = m_systemd.scopePids(unitPath);
|
||||||
|
if (!pids.isEmpty()) {
|
||||||
|
sit.value().mainPid = pids.first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const QString unitPathKey = unitPath.path();
|
||||||
|
m_unitPathToSessionId.remove(unitPathKey);
|
||||||
|
if (QObject *watcher = m_unitPathWatchers.take(unitPathKey)) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
}
|
||||||
|
sit.value().unitPath = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT SessionChanged(sessionToVariantMap(sit.value(), sessionState(sit.value())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (activeState == QLatin1String("active") || activeState == QLatin1String("activating") || activeState == QLatin1String("deactivating")) {
|
if (activeState == QLatin1String("active") || activeState == QLatin1String("activating") || activeState == QLatin1String("deactivating")) {
|
||||||
const QList<uint> pids = m_systemd.scopePids(unitPath);
|
const QList<uint> pids = m_systemd.scopePids(unitPath);
|
||||||
if (!pids.isEmpty()) {
|
if (!pids.isEmpty()) {
|
||||||
|
|
@ -1495,6 +1526,8 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
||||||
|
|
||||||
m_sessions[sessionId] = session;
|
m_sessions[sessionId] = session;
|
||||||
|
|
||||||
|
watchSystemdUnit(sessionId, session.unitName, session.unitPath);
|
||||||
|
|
||||||
connect(bootstrap, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, [this, sessionId](int, QProcess::ExitStatus) {
|
connect(bootstrap, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, [this, sessionId](int, QProcess::ExitStatus) {
|
||||||
const auto it = m_sessions.find(sessionId);
|
const auto it = m_sessions.find(sessionId);
|
||||||
if (it == m_sessions.end()) {
|
if (it == m_sessions.end()) {
|
||||||
|
|
@ -1609,6 +1642,11 @@ void GameCenterDaemon::attachPidsToSession(const QString &sessionId, const QList
|
||||||
{QStringLiteral("error"), attachReply.error().message()},
|
{QStringLiteral("error"), attachReply.error().message()},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!it.value().unitName.isEmpty()) {
|
||||||
|
m_systemd.stopUnit(it.value().unitName);
|
||||||
|
unwatchSystemdUnit(it.value().unitName, it.value().unitPath);
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap finalState = sessionToVariantMap(it.value(), QStringLiteral("Failed"));
|
QVariantMap finalState = sessionToVariantMap(it.value(), QStringLiteral("Failed"));
|
||||||
if (it.value().process) {
|
if (it.value().process) {
|
||||||
it.value().process->deleteLater();
|
it.value().process->deleteLater();
|
||||||
|
|
@ -1780,6 +1818,32 @@ void GameCenterDaemon::Stop(const QString &sessionId)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDBusObjectPath unitPath = it.value().unitPath;
|
||||||
|
if (unitPath.path().isEmpty()) {
|
||||||
|
const QDBusReply<QDBusObjectPath> getUnitReply = m_systemd.getUnit(it.value().unitName);
|
||||||
|
if (getUnitReply.isValid()) {
|
||||||
|
unitPath = getUnitReply.value();
|
||||||
|
it.value().unitPath = unitPath;
|
||||||
|
watchSystemdUnit(sessionId, it.value().unitName, unitPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<uint> pids;
|
||||||
|
if (!unitPath.path().isEmpty()) {
|
||||||
|
pids = m_systemd.scopePids(unitPath);
|
||||||
|
}
|
||||||
|
if (pids.isEmpty() && it.value().mainPid > 0) {
|
||||||
|
pids = {it.value().mainPid};
|
||||||
|
}
|
||||||
|
if (!pids.isEmpty()) {
|
||||||
|
terminatePids(pids);
|
||||||
|
QTimer::singleShot(5000, this, [pids]() {
|
||||||
|
killPids(pids);
|
||||||
|
});
|
||||||
|
Q_EMIT SessionChanged(sessionToVariantMap(it.value(), QStringLiteral("Stopping")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (it.value().process && it.value().process->state() != QProcess::NotRunning) {
|
if (it.value().process && it.value().process->state() != QProcess::NotRunning) {
|
||||||
it.value().process->terminate();
|
it.value().process->terminate();
|
||||||
QTimer::singleShot(5000, it.value().process, [process = it.value().process]() {
|
QTimer::singleShot(5000, it.value().process, [process = it.value().process]() {
|
||||||
|
|
@ -1886,7 +1950,8 @@ void GameCenterDaemon::recoverExistingSessions()
|
||||||
|
|
||||||
static const QString prefix = QStringLiteral("alakarte-game-");
|
static const QString prefix = QStringLiteral("alakarte-game-");
|
||||||
static const QString suffix = QStringLiteral(".scope");
|
static const QString suffix = QStringLiteral(".scope");
|
||||||
static const QString descPrefix = QStringLiteral("A-La-Karte game ");
|
static const QString descBase = QStringLiteral("A-La-Karte game");
|
||||||
|
static const QString descPrefix = descBase + QLatin1Char(' ');
|
||||||
|
|
||||||
const SystemdUnitInfoList units = reply.value();
|
const SystemdUnitInfoList units = reply.value();
|
||||||
for (const SystemdUnitInfo &unit : units) {
|
for (const SystemdUnitInfo &unit : units) {
|
||||||
|
|
@ -1897,6 +1962,10 @@ void GameCenterDaemon::recoverExistingSessions()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unit.description != descBase && !unit.description.startsWith(descPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const QString sessionId = unit.name.mid(prefix.length(), unit.name.length() - prefix.length() - suffix.length());
|
const QString sessionId = unit.name.mid(prefix.length(), unit.name.length() - prefix.length() - suffix.length());
|
||||||
if (sessionId.isEmpty() || m_sessions.contains(sessionId)) {
|
if (sessionId.isEmpty() || m_sessions.contains(sessionId)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue