[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.