[plasma/krdp] /: Optionally lock the session when the last client disconnects
David Edmundson <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit b10a0fa0fc9575577e028c0fca7d3f9482622e8f by David Edmundson, on behalf of Nick Haghiri.
Committed on 22/07/2026 at 08:06.
Pushed by davidedmundson into branch 'master'.
Optionally lock the session when the last client disconnects
Add a LockOnDisconnect setting, exposed in the KCM, that locks the desktop via logind as the last RDP client disconnects and unlocks it when a client connects. Off by default; a no-op unless enabled.
The session is resolved from $XDG_SESSION_ID (falling back to the process's PID, since krdpserver is a user-service process rather than a login-session process) and locked/unlocked through org.freedesktop.login1, which kscreenlocker already honours.
M +39 -0 server/SessionController.cpp
M +11 -0 server/SessionController.h
M +1 -0 server/main.cpp
M +4 -0 src/kcm/krdpserversettings.kcfg
M +13 -0 src/kcm/ui/main.qml
https://invent.kde.org/plasma/krdp/-/commit/b10a0fa0fc9575577e028c0fca7d3f9482622e8f
diff --git a/server/SessionController.cpp b/server/SessionController.cpp
index 4039e01..f5087df 100644
--- a/server/SessionController.cpp
+++ b/server/SessionController.cpp
@@ -8,6 +8,7 @@
#include <QAction>
#include <QCoreApplication>
#include <QDBusInterface>
+#include <QDBusReply>
#include <QMenu>
#include <KLocalizedString>
@@ -132,6 +133,37 @@ void SessionController::setQuality(const std::optional<int> &quality)
m_quality = quality;
}
+void SessionController::setLockOnDisconnect(bool lock)
+{
+ m_lockOnDisconnect = lock;
+}
+
+void SessionController::setSessionLocked(bool locked)
+{
+ if (!m_lockOnDisconnect) {
+ return;
+ }
+ // Ask logind to lock/unlock the graphical session (kscreenlocker honours its
+ // Lock/Unlock signals). krdpserver is a user-service process not in a login session,
+ // so resolve the session from $XDG_SESSION_ID, falling back to our PID.
+ auto bus = QDBusConnection::systemBus();
+ QDBusInterface manager(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s, u"org.freedesktop.login1.Manager"_s, bus);
+ QDBusReply<QDBusObjectPath> session;
+ const QString sessionId = qEnvironmentVariable("XDG_SESSION_ID");
+ if (!sessionId.isEmpty()) {
+ session = manager.call(u"GetSession"_s, sessionId);
+ }
+ if (!session.isValid()) {
+ session = manager.call(u"GetSessionByPID"_s, static_cast<quint32>(QCoreApplication::applicationPid()));
+ }
+ if (!session.isValid()) {
+ qWarning() << "krdp: could not resolve a logind session to" << (locked ? "lock" : "unlock") << ":" << session.error().message();
+ return;
+ }
+ QDBusInterface sessionIface(u"org.freedesktop.login1"_s, session.value().path(), u"org.freedesktop.login1.Session"_s, bus);
+ sessionIface.call(locked ? u"Lock"_s : u"Unlock"_s);
+}
+
void SessionController::onNewConnection(KRdp::RdpConnection *newConnection)
{
auto wrapper = std::make_unique<SessionWrapper>(newConnection, makeSession(), m_sni);
@@ -141,9 +173,16 @@ void SessionController::onNewConnection(KRdp::RdpConnection *newConnection)
wrapper->session->setActiveStream(*m_monitorIndex);
}
wrapper->connection->videoStream()->setVideoQuality(m_quality.value());
+ // A client is taking over; unlock the session so it sees the desktop.
+ setSessionLocked(false);
wrapper->session->start();
connect(wrapper.get(), &SessionWrapper::connectionDestroyed, this, [this](SessionWrapper *wrapper) {
+ // Lock the machine as the last client leaves.
+ if (m_wrappers.size() == 1 && m_wrappers.front().get() == wrapper) {
+ setSessionLocked(true);
+ }
+
m_wrappers.erase(std::remove_if(m_wrappers.begin(),
m_wrappers.end(),
[wrapper](std::unique_ptr<SessionWrapper> &entry) {
diff --git a/server/SessionController.h b/server/SessionController.h
index 98f514d..8e3dc25 100644
--- a/server/SessionController.h
+++ b/server/SessionController.h
@@ -37,9 +37,18 @@ public:
void setSNIStatus(const KRdp::RdpConnection::State state);
void stopFromSNI();
+ /**
+ * When enabled, lock the desktop session as the last client disconnects and
+ * unlock it (via logind) when a client connects, so the physical machine is
+ * left locked while no one is using it remotely.
+ */
+ void setLockOnDisconnect(bool lock);
+
private:
void onNewConnection(KRdp::RdpConnection *newConnection);
std::unique_ptr<KRdp::AbstractSession> makeSession();
+ // Lock/unlock the desktop session via logind (no-op unless setLockOnDisconnect(true)).
+ void setSessionLocked(bool locked);
KRdp::Server *m_server = nullptr;
SessionType m_sessionType;
@@ -51,5 +60,7 @@ private:
std::vector<std::unique_ptr<SessionWrapper>> m_wrappers;
+ bool m_lockOnDisconnect = false;
+
KStatusNotifierItem *m_sni;
};
diff --git a/server/main.cpp b/server/main.cpp
index 5389cdf..a5a14fc 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -139,6 +139,7 @@ int main(int argc, char **argv)
controller.setMonitorIndex(parser.isSet(u"monitor"_s) ? std::optional(parser.value(u"monitor"_s).toInt()) : std::nullopt);
}
controller.setQuality(parserValueWithDefault(u"quality", config->quality()));
+ controller.setLockOnDisconnect(config->lockOnDisconnect());
if (!server.start()) {
return -1;
diff --git a/src/kcm/krdpserversettings.kcfg b/src/kcm/krdpserversettings.kcfg
index f184c23..f9c3813 100644
--- a/src/kcm/krdpserversettings.kcfg
+++ b/src/kcm/krdpserversettings.kcfg
@@ -39,5 +39,9 @@ SPDX-License-Identifier: BSD-2-Clause
<label>Autostart the server on login</label>
<default>false</default>
</entry>
+ <entry name="LockOnDisconnect" type="Bool">
+ <label>Lock the session when the last client disconnects and unlock it on connect</label>
+ <default>false</default>
+ </entry>
</group>
</kcfg>
diff --git a/src/kcm/ui/main.qml b/src/kcm/ui/main.qml
index d6b0f5f..e036d09 100644
--- a/src/kcm/ui/main.qml
+++ b/src/kcm/ui/main.qml
@@ -270,6 +270,19 @@ KCM.ScrollViewKCM {
}
}
+ QQC2.CheckBox {
+ id: lockOnDisconnect
+ text: i18nc("@option:check", "Lock the session when disconnecting, unlock on connect")
+ checked: settings.lockOnDisconnect
+ onToggled: {
+ settings.lockOnDisconnect = checked;
+ }
+ KCM.SettingStateBinding {
+ configObject: settings
+ settingName: "lockOnDisconnect"
+ }
+ }
+
ColumnLayout {
enabled: userListView.count > 0
Layout.preferredWidth: certKeyLayout.width