1#include "kiogui_debug.h"
2#include "managerinterface.h"
3#include "scopedprocessrunner_p.h"
4#include "systemdprocessrunner_p.h"
5
6#include <sys/eventfd.h>
7#include <unistd.h>
8
9using namespace org::freedesktop;
10
11ScopedProcessRunner::ScopedProcessRunner()
12 : ForkingProcessRunner()
13{
14}
15
16void ScopedProcessRunner::startProcess()
17{
18 std::function oldModifier = m_process->childProcessModifier();
19 int efd = eventfd(count: 0, EFD_CLOEXEC);
20 m_process->setChildProcessModifier([efd, oldModifier]() {
21 // wait for the parent process to be done registering the transient unit
22 eventfd_read(fd: efd, value: nullptr);
23 if (oldModifier)
24 oldModifier();
25 });
26
27 // actually start
28 ForkingProcessRunner::startProcess();
29 m_process->setChildProcessModifier(oldModifier);
30
31 Q_ASSERT(m_process->processId());
32
33 // As specified in "XDG standardization for applications" in https://systemd.io/DESKTOP_ENVIRONMENTS/
34 const QString serviceName = QStringLiteral("app-%1-%2.scope").arg(args: escapeUnitName(input: resolveServiceAlias()), args: QUuid::createUuid().toString(mode: QUuid::Id128));
35
36 const auto manager = new systemd1::Manager(systemdService, systemdPath, QDBusConnection::sessionBus(), this);
37
38 // Ask systemd for a new transient service
39 const auto startReply =
40 manager->StartTransientUnit(name: serviceName,
41 QStringLiteral("fail"), // mode defines what to do in the case of a name conflict, in this case, just do nothing
42 properties: {// Properties of the transient service unit
43 {QStringLiteral("Slice"), QStringLiteral("app.slice")},
44 {QStringLiteral("Description"), .value: m_description},
45 {QStringLiteral("SourcePath"), .value: m_desktopFilePath},
46 {QStringLiteral("PIDs"), .value: QVariant::fromValue(value: QList<uint>{static_cast<uint>(m_process->processId())})}},
47 aux: {} // aux is currently unused and should be passed as empty array.
48 );
49
50 m_transientUnitStartupwatcher = new QDBusPendingCallWatcher(startReply, this);
51 connect(sender: m_transientUnitStartupwatcher, signal: &QDBusPendingCallWatcher::finished, slot: [serviceName, efd](QDBusPendingCallWatcher *watcher) {
52 QDBusPendingReply<QDBusObjectPath> reply = *watcher;
53 watcher->deleteLater();
54 if (reply.isError()) {
55 qCWarning(KIO_GUI) << "Failed to register new cgroup:" << serviceName << reply.error().name() << reply.error().message();
56 } else {
57 qCDebug(KIO_GUI) << "Successfully registered new cgroup:" << serviceName;
58 }
59
60 // release child and close the eventfd
61 eventfd_write(fd: efd, value: 1);
62 close(fd: efd);
63 });
64}
65
66bool ScopedProcessRunner::waitForStarted(int timeout)
67{
68 if (m_process->state() == QProcess::NotRunning || m_waitingForXdgToken || !m_transientUnitStartupwatcher->isFinished()) {
69 QEventLoop loop;
70 QObject::connect(sender: m_process.get(), signal: &QProcess::stateChanged, context: &loop, slot: &QEventLoop::quit);
71 QObject::connect(sender: m_transientUnitStartupwatcher, signal: &QDBusPendingCallWatcher::finished, context: &loop, slot: &QEventLoop::quit);
72 QTimer::singleShot(interval: timeout, receiver: &loop, slot: &QEventLoop::quit);
73 loop.exec();
74 }
75 return m_process->waitForStarted(msecs: timeout);
76}
77
78#include "moc_scopedprocessrunner_p.cpp"
79

source code of kio/src/gui/systemd/scopedprocessrunner.cpp