diff --git a/src/gui/kprocessrunner.cpp b/src/gui/kprocessrunner.cpp --- a/src/gui/kprocessrunner.cpp +++ b/src/gui/kprocessrunner.cpp @@ -30,11 +30,17 @@ #include #include +#include #include +#include +#include #include #include #include #include +#include + +#include static int s_instanceCount = 0; // for the unittest @@ -177,6 +183,12 @@ Q_UNUSED(userVisibleName); Q_UNUSED(iconName); #endif + if (service) { + m_scopeId = service->desktopEntryName(); + } + if (m_scopeId.isEmpty()) { + m_scopeId = m_executable; + } startProcess(); } @@ -209,6 +221,7 @@ void KProcessRunner::slotProcessStarted() { m_pid = m_process->processId(); + registerCGroup(); #if HAVE_X11 if (!m_startupId.isNull() && m_pid) { @@ -266,6 +279,60 @@ deleteLater(); } +void KProcessRunner::registerCGroup() +{ +#ifdef Q_OS_LINUX + if (!qEnvironmentVariableIsSet("KDE_APPLICATIONS_AS_SCOPE")) { + return; + } + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.systemd1"))) { + return; + } + + typedef QPair NamedVariant; + typedef QList NamedVariantList; + + static std::once_flag dbusTypesRegistered; + std::call_once(dbusTypesRegistered, []() { + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType>(); + qDBusRegisterMetaType>>(); + }); + + QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("StartTransientUnit")); + + const QString name = QStringLiteral("apps-%1-%2.scope").arg(m_scopeId, QUuid::createUuid().toString(QUuid::Id128)); + // mode defines what to do in the case of a name conflict, in this case, just do nothing + const QString mode = QStringLiteral("fail"); + + const QList pidList = {static_cast(m_process->pid())}; + + NamedVariantList properties = {NamedVariant({QStringLiteral("PIDs"), QDBusVariant(QVariant::fromValue(pidList))})}; + + QList> aux; + + message.setArguments({name, mode, QVariant::fromValue(properties), QVariant::fromValue(aux)}); + QDBusPendingCall reply = QDBusConnection::sessionBus().asyncCall(message); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, qApp, [=]() { + watcher->deleteLater(); + if (reply.isError()) { + qCWarning(KIO_GUI) << "Failed to register new cgroup:" << name; + } else { + qCDebug(KIO_GUI) << "Successfully registered new cgroup:" << name; + } + }); + +#endif +} + + // This code is also used in klauncher (and KRun). bool KIOGuiPrivate::checkStartupNotify(const KService *service, bool *silent_arg, QByteArray *wmclass_arg) { diff --git a/src/gui/kprocessrunner_p.h b/src/gui/kprocessrunner_p.h --- a/src/gui/kprocessrunner_p.h +++ b/src/gui/kprocessrunner_p.h @@ -107,12 +107,14 @@ void init(const KService::Ptr &service, const QString &userVisibleName, const QString &iconName, const QByteArray &asn); void startProcess(); + void registerCGroup(); void terminateStartupNotification(); void emitDelayedError(const QString &errorMsg); std::unique_ptr m_process; QString m_executable; // can be a full path KStartupInfoId m_startupId; + QString m_scopeId; qint64 m_pid = 0; Q_DISABLE_COPY(KProcessRunner)