systemd: Set up a watchdog

Allows to notify systemd whether kwin is still running and possibly
restart the service if it stops responding.

Use Type=notify-reload to watch the kwin service. This will make it so
we receive SIGHUP rather than SIGTERM on the wrapper which we can handle
gracefully and stop the kwin process and restart as expected.

https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html

Signed-off-by: Victoria Fischer <victoria.fischer@mbition.io>
wilder/Plasma/6.2
Aleix Pol Gonzalez 2 years ago committed by Vlad Zahorodnii
parent aac5d562fb
commit 71ade59f4b
  1. 3
      CMakeLists.txt
  2. 4
      plasma-kwin_wayland.service.in
  3. 5
      src/CMakeLists.txt
  4. 41
      src/helpers/wayland_wrapper/kwin_wrapper.cpp
  5. 62
      src/watchdog.cpp

@ -355,6 +355,9 @@ set_package_properties(QAccessibilityClient6 PROPERTIES
)
set(HAVE_ACCESSIBILITY ${QAccessibilityClient6_FOUND})
pkg_check_modules(libsystemd IMPORTED_TARGET libsystemd)
add_feature_info(libsystemd libsystemd_FOUND "Required for setting up the service watchdog")
option(KWIN_BUILD_GLOBALSHORTCUTS "Enable building of KWin with global shortcuts support" ON)
if(KWIN_BUILD_GLOBALSHORTCUTS)
find_package(KGlobalAccelD REQUIRED)

@ -3,6 +3,10 @@ Description=KDE Window Manager
PartOf=graphical-session.target
[Service]
Type=notify-reload
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/kwin_wayland_wrapper --xwayland
BusName=org.kde.KWinWrapper
Slice=session.slice
WatchdogSec=5s
NotifyAccess=all
WatchdogSignal=SIGHUP

@ -355,6 +355,11 @@ if (KWIN_BUILD_TABBOX)
add_subdirectory(tabbox/switchers)
endif()
if(TARGET PkgConfig::libsystemd)
target_sources(kwin PRIVATE watchdog.cpp)
target_link_libraries(kwin PRIVATE PkgConfig::libsystemd)
endif()
qt_generate_dbus_interface(virtualkeyboard_dbus.h org.kde.kwin.VirtualKeyboard.xml OPTIONS -A)
qt_generate_dbus_interface(tabletmodemanager.h org.kde.KWin.TabletModeManager.xml OPTIONS -A)

@ -42,7 +42,10 @@ class KWinWrapper : public QObject
public:
KWinWrapper(QObject *parent);
~KWinWrapper();
void run();
void restart();
void terminate();
private:
wl_socket *m_socket;
@ -82,13 +85,7 @@ KWinWrapper::KWinWrapper(QObject *parent)
KWinWrapper::~KWinWrapper()
{
wl_socket_destroy(m_socket);
if (m_kwinProcess) {
disconnect(m_kwinProcess, nullptr, this, nullptr);
m_kwinProcess->terminate();
m_kwinProcess->waitForFinished();
m_kwinProcess->kill();
m_kwinProcess->waitForFinished();
}
terminate();
}
void KWinWrapper::run()
@ -147,7 +144,6 @@ void KWinWrapper::run()
env.insert("XAUTHORITY", m_xauthorityFile.fileName());
}
}
auto envSyncJob = new KUpdateLaunchEnvironmentJob(env);
connect(envSyncJob, &KUpdateLaunchEnvironmentJob::finished, this, []() {
// The service name is merely there to indicate to the world that we're up and ready with all envs exported
@ -155,21 +151,42 @@ void KWinWrapper::run()
});
}
void KWinWrapper::terminate()
{
if (m_kwinProcess) {
disconnect(m_kwinProcess, nullptr, this, nullptr);
m_kwinProcess->terminate();
m_kwinProcess->waitForFinished();
m_kwinProcess->kill();
m_kwinProcess->waitForFinished();
}
}
void KWinWrapper::restart()
{
terminate();
m_kwinProcess->start();
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
app.setQuitLockEnabled(false); // don't exit when the first KJob finishes
KSignalHandler::self()->watchSignal(SIGTERM);
QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app](int signal) {
KSignalHandler::self()->watchSignal(SIGHUP);
KWinWrapper wrapper(&app);
wrapper.run();
QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app, &wrapper](int signal) {
if (signal == SIGTERM) {
app.quit();
} else if (signal == SIGHUP) { // The systemd service will issue SIGHUP when it's locked up so that we can restarted
wrapper.restart();
}
});
KWinWrapper wrapper(&app);
wrapper.run();
return app.exec();
}

@ -0,0 +1,62 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2024 Aleix Pol i Gonzalez <aleix.pol_gonzalez@mbition.io>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "utils/common.h"
#include <QCoreApplication>
#include <QTimer>
#include <systemd/sd-daemon.h>
class Watchdog : public QObject
{
Q_OBJECT
public:
Watchdog(QObject *parent)
: QObject(parent)
{
bool ok;
const std::chrono::microseconds watchdogIntervalInUs((qgetenv("WATCHDOG_USEC").toUInt(&ok) * 3) / 4);
if (!ok) {
qCInfo(KWIN_CORE) << "Watchdog: disabled, not running on a systemd environment or watchdog is not set up. No WATCHDOG_USEC.";
deleteLater();
return;
}
m_onBehalf = qgetenv("WATCHDOG_PID").toUInt(&ok);
if (!ok) {
qCInfo(KWIN_CORE) << "Watchdog: disabled, not running on a systemd environment or watchdog is not set up. No WATCHDOG_PID.";
deleteLater();
return;
}
qunsetenv("WATCHDOG_USEC");
qunsetenv("WATCHDOG_PID");
auto t = new QTimer(this);
t->setInterval(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogIntervalInUs));
t->setSingleShot(false);
qCInfo(KWIN_CORE) << "Watchdog: enabled. Interval:" << watchdogIntervalInUs << t->intervalAsDuration();
sd_pid_notify(m_onBehalf, 0, "READY=1"); // If service Type=notify the service is only considered ready once we send this
qCInfo(KWIN_CORE) << "Watchdog: Notified as ready";
connect(t, &QTimer::timeout, this, [this] {
sd_pid_notify(m_onBehalf, 0, "WATCHDOG=1");
});
t->start();
}
private:
pid_t m_onBehalf = 0;
};
static void setupWatchdog()
{
new Watchdog(QCoreApplication::instance());
}
Q_COREAPP_STARTUP_FUNCTION(setupWatchdog)
#include "watchdog.moc"
Loading…
Cancel
Save