[plasma/kscreenlocker] greeter: get rid of pam worker

Harald Sitter <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 02c33426d884323e2baf63f203f5e4d8e79bf6e6 by Harald Sitter, on behalf of David Edmundson.
Committed on 18/08/2026 at 11:15.
Pushed by sitter into branch 'master'.

get rid of pam worker

M  +1    -2    greeter/CMakeLists.txt
M  +197  -84   greeter/pamauthenticator.cpp
M  +23   -19   greeter/pamauthenticator.h
D  +0    -214  greeter/pamworker.cpp
D  +0    -65   greeter/pamworker.h

https://invent.kde.org/plasma/kscreenlocker/-/commit/02c33426d884323e2baf63f203f5e4d8e79bf6e6

diff --git a/greeter/CMakeLists.txt b/greeter/CMakeLists.txt
index 64726387..c29f786a 100644
--- a/greeter/CMakeLists.txt
+++ b/greeter/CMakeLists.txt
@@ -14,7 +14,6 @@ set(kscreenlocker_authenticator_SRCS
     pamauthenticator.cpp
     pamauthenticator.h
     pamauthenticatormodel.cpp
-    pamworker.cpp
     pamauthenticatordescriptor.cpp
 )
 
@@ -46,7 +45,7 @@ ecm_qt_declare_logging_category(kscreenlocker_greet_SRCS
 
 set_source_files_properties(worker/org.kde.plasma.screenlocker.worker.xml PROPERTIES INCLUDE "result.h")
 qt_add_dbus_interface(kscreenlocker_authenticator_SRCS worker/org.kde.plasma.screenlocker.worker.xml org.kde.plasma.screenlocker.worker)
-qt_add_dbus_adaptor(kscreenlocker_authenticator_SRCS worker/org.kde.plasma.screenlocker.xml pamworker.h PamWorker)
+qt_add_dbus_adaptor(kscreenlocker_authenticator_SRCS worker/org.kde.plasma.screenlocker.xml pamauthenticator.h PamAuthenticator)
 
 add_library(kscreenlocker_authenticator OBJECT ${kscreenlocker_authenticator_SRCS})
 target_link_libraries(kscreenlocker_authenticator
diff --git a/greeter/pamauthenticator.cpp b/greeter/pamauthenticator.cpp
index 833405da..b343b87a 100644
--- a/greeter/pamauthenticator.cpp
+++ b/greeter/pamauthenticator.cpp
@@ -7,11 +7,19 @@
 
 #include "pamauthenticator.h"
 
-#include <QElapsedTimer>
+#include <QDBusConnection>
+#include <QDBusPendingCallWatcher>
+#include <QDBusServer>
 #include <QMetaMethod>
-#include <QThread>
+#include <QProcess>
+#include <QTimer>
 
-#include "pamworker.h"
+#include <KLibexec>
+
+#include "kscreenlocker_greet_logging.h"
+#include "org.kde.plasma.screenlocker.worker.h"
+#include "result.h"
+#include "screenlockeradaptor.h"
 
 using namespace std::chrono_literals;
 using namespace Qt::StringLiterals;
@@ -26,75 +34,15 @@ PamAuthenticator::PamAuthenticator(const QString &service, const QString &user,
       })
     , m_service(service)
     , m_authenticatorType(types)
-    , m_thread(std::make_unique<PamWorker>(service, user))
-    , d(m_thread.worker())
+    , m_user(user)
 {
-    connect(d, &PamWorker::busyChanged, this, &PamAuthenticator::setBusy);
-    connect(d, &PamWorker::inPasswordDelayChanged, this, &PamAuthenticator::setInPasswordDelay);
-    connect(d, &PamWorker::prompt, this, [this](const QString &msg) {
-        m_prompt = msg;
-        Q_EMIT prompt(msg);
-    });
-    connect(d, &PamWorker::promptForSecret, this, [this](const QString &msg) {
-        m_promptForSecret = msg;
-        Q_EMIT promptForSecret(msg);
-    });
-    connect(d, &PamWorker::infoMessage, this, [this](const QString &msg) {
-        m_infoMessage = msg;
-        Q_EMIT infoMessage(msg);
-    });
-    connect(d, &PamWorker::errorMessage, this, [this](const QString &msg) {
-        m_errorMessage = msg;
-        Q_EMIT errorMessage(msg);
-    });
-
-    connect(d, &PamWorker::inAuthenticateChanged, this, [this] {
-        Q_EMIT availableChanged();
-    });
-    connect(d, &PamWorker::unavailabilityChanged, this, [this](bool isUnavailable) {
-        m_unavailable = isUnavailable;
-        Q_EMIT availableChanged();
-    });
-
-    connect(d, &PamWorker::succeeded, this, [this]() {
-        m_unlocked = true;
-        Q_EMIT succeeded();
-    });
-    // Failed is not a persistent state. When a view provides authentication that will either result in failure or success,
-    // failure simply means that the prompt is getting delayed.
-    connect(d, &PamWorker::failed, this, [this] {
-        // Guard against particularly broken PAM services. For example when pam-u2f doesn't find a token because it is
-        // not plugged in it will fail the authentication, but it will do it so slowly that the timing checks in the
-        // worker itself don't bite.
-        // Here we can keep a higher level view of the failures and if need be break the loop by marking us unavailable.
-        auto now = QDateTime::currentDateTimeUtc();
-        if (now - m_lastFailed < 2s) {
-            m_failedCount++;
-            if (m_failedCount > 3) {
-                m_unavailable = true;
-                Q_EMIT availableChanged();
-            }
-        } else {
-            m_failedCount = 0;
-        }
-        m_lastFailed = now;
-
-        Q_EMIT failed();
-    });
-    connect(d, &PamWorker::loginFailedDelayStarted, this, &PamAuthenticator::loginFailedDelayStarted);
-
-    m_thread.start();
-
-    QMetaObject::invokeMethod(d, [this]() {
-        d->start();
-    });
+    new ScreenlockerAdaptor(this);
+    startWorker();
 }
 
 PamAuthenticator::~PamAuthenticator()
 {
-    // This is a special thread type that cleans up the worker before returning to us.
-    m_thread.quit();
-    m_thread.wait();
+    quitWorkerProcess();
 }
 
 bool PamAuthenticator::isBusy() const
@@ -128,17 +76,78 @@ bool PamAuthenticator::isUnlocked() const
 void PamAuthenticator::tryUnlock()
 {
     m_unlocked = false;
-    QMetaObject::invokeMethod(d, &PamWorker::authenticate);
+
+    if (m_busy) {
+        return;
+    }
+
+    if (!m_dbusWorker) {
+        qCWarning(KSCREENLOCKER_GREET) << "DBus worker not initialized, cannot authenticate yet.";
+        return;
+    }
+
+    if (m_unavailable) {
+        qCDebug(KSCREENLOCKER_GREET) << "PAM service is not available. Cannot authenticate.";
+        return;
+    }
+
+    setBusy(true);
+    auto watcher = new QDBusPendingCallWatcher(m_dbusWorker->Authenticate(), this);
+    connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() {
+        watcher->deleteLater();
+        setBusy(false);
+
+        if (watcher->error().isValid()) {
+            qCWarning(KSCREENLOCKER_GREET) << "PAM worker failed to authenticate:" << watcher->error().message();
+            Q_EMIT failed();
+            return;
+        }
+
+        decltype(m_dbusWorker->Authenticate()) reply = watcher->reply();
+        switch (reply.value()) {
+        case WorkerResult::Type::Failure: {
+            // Guard against particularly broken PAM services. For example when pam-u2f doesn't find a token because it is
+            // not plugged in it will fail the authentication, but it will do it so slowly that the timing checks in the
+            // worker itself don't bite.
+            // Here we can keep a higher level view of the failures and if need be break the loop by marking us unavailable.
+            auto now = QDateTime::currentDateTimeUtc();
+            if (now - m_lastFailed < 2s) {
+                m_failedCount++;
+                if (m_failedCount > 3) {
+                    m_unavailable = true;
+                    Q_EMIT availableChanged();
+                }
+            } else {
+                m_failedCount = 0;
+            }
+            m_lastFailed = now;
+
+            Q_EMIT failed();
+            return;
+        }
+        case WorkerResult::Type::Success:
+            m_unlocked = true;
+            Q_EMIT succeeded();
+            return;
+        case WorkerResult::Type::Unavailable:
+            m_unavailable = true;
+            Q_EMIT availableChanged();
+            return;
+        };
+
+        qWarning() << "Unexpected authentication result" << reply.value();
+    });
 }
 
 void PamAuthenticator::respond(const QByteArray &response)
 {
-    QMetaObject::invokeMethod(
-        d,
-        [this, response]() {
-            Q_EMIT d->promptResponseReceived(response);
-        },
-        Qt::QueuedConnection);
+    if (m_pendingPrompt.type() != QDBusMessage::InvalidMessage && m_dbusWorker) {
+        QDBusMessage reply = m_pendingPrompt.createReply(QString::fromUtf8(response));
+        m_dbusWorker->connection().send(reply);
+        m_pendingPrompt = {};
+    } else {
+        qCWarning(KSCREENLOCKER_GREET) << "Received prompt response, but no pending prompt was found!";
+    }
 }
 
 void PamAuthenticator::cancel()
@@ -147,7 +156,18 @@ void PamAuthenticator::cancel()
     m_promptForSecret.clear();
     m_infoMessage.clear();
     m_errorMessage.clear();
-    QMetaObject::invokeMethod(d, &PamWorker::cancelled);
+
+    if (!m_dbusWorker) {
+        return;
+    }
+
+    // Timings here are tight because we may need to cancel two workers, we don't want to get stuck on this for too long.
+    // Let it quit on its own. Mind that this usually will get stuck because the worker is itself waiting for a prompt response.
+    std::ignore = m_dbusWorker->Cancel();
+    if (m_workerProcess) {
+        m_workerProcess->waitForFinished((5ms).count());
+    }
+    quitWorkerProcess();
 }
 
 QString PamAuthenticator::getPrompt() const
@@ -186,20 +206,113 @@ void PamAuthenticator::setInPasswordDelay(bool timeout)
     Q_EMIT inPasswordDelayChanged();
 }
 
-PamAuthenticator::WorkerThread::WorkerThread(std::unique_ptr<PamWorker> &&worker, QObject *parent)
-    : QThread(parent)
-    , m_worker(std::move(worker))
+void PamAuthenticator::Ping(const QString &message)
+{
+    qCDebug(KSCREENLOCKER_GREET) << "Ping received" << message << calledFromDBus();
+    if (!m_dbusWorker) {
+        qCWarning(KSCREENLOCKER_GREET) << "Worker pinged before DBus worker interface was initialized.";
+        return;
+    }
+
+    auto watcher = new QDBusPendingCallWatcher(m_dbusWorker->Start(m_service, m_user), this);
+    connect(watcher, &QDBusPendingCallWatcher::finished, this, [watcher]() {
+        watcher->deleteLater();
+        Q_ASSERT(watcher->isValid());
+    });
+}
+
+QString PamAuthenticator::Prompt(const QString &msg)
+{
+    return promptInternal(msg, false);
+}
+
+QString PamAuthenticator::MaskedPrompt(const QString &msg)
+{
+    return promptInternal(msg, true);
+}
+
+void PamAuthenticator::StartFailedDelay(uint useconds)
+{
+    setInPasswordDelay(true);
+    QTimer::singleShot(std::chrono::microseconds(useconds), this, [this]() {
+        setInPasswordDelay(false);
+    });
+    Q_EMIT loginFailedDelayStarted(useconds);
+}
+
+void PamAuthenticator::InfoMessage(const QString &msg)
+{
+    m_infoMessage = msg;
+    Q_EMIT infoMessage(msg);
+}
+
+void PamAuthenticator::ErrorMessage(const QString &msg)
 {
-    m_worker->moveToThread(this);
+    m_errorMessage = msg;
+    Q_EMIT errorMessage(msg);
 }
 
-[[nodiscard]] PamWorker *PamAuthenticator::WorkerThread::worker() const
+QString PamAuthenticator::promptInternal(const QString &msg, bool isSecret)
 {
-    return m_worker.get();
+    setBusy(false);
+
+    if (isSecret) {
+        m_promptForSecret = msg;
+        Q_EMIT promptForSecret(msg);
+    } else {
+        m_prompt = msg;
+        Q_EMIT prompt(msg);
+    }
+
+    qCDebug(KSCREENLOCKER_GREET,
+            "[PAM worker %s] Message: %s: %s",
+            qUtf8Printable(m_service),
+            (isSecret ? "Echo-off prompt" : "Echo-on prompt"),
+            qUtf8Printable(msg));
+
+    setDelayedReply(true);
+    m_pendingPrompt = message();
+
+    return {};
+}
+
+void PamAuthenticator::quitWorkerProcess()
+{
+    if (m_workerProcess) {
+        m_workerProcess->terminate();
+        if (!m_workerProcess->waitForFinished((25ms).count())) {
+            qWarning() << "Worker did not terminate in time, killing it.";
+            m_workerProcess->kill();
+        }
+    }
+    m_dbusWorker.reset();
+    m_workerProcess = nullptr;
+}
+
+void PamAuthenticator::startWorker()
+{
+    Q_ASSERT(!m_workerProcess);
+
+    qCDebug(KSCREENLOCKER_GREET) << "Starting PAM worker for service" << m_service << "and user" << m_user;
+
+    m_server = new QDBusServer(this);
+    connect(m_server, &QDBusServer::newConnection, this, &PamAuthenticator::connectWorker);
+
+    m_workerProcess = new QProcess(this);
+    m_workerProcess->setProcessChannelMode(QProcess::ForwardedChannels);
+    m_workerProcess->setProgram(KLibexec::path(u"kscreenlocker_worker"_s));
+    m_workerProcess->setArguments({m_service});
+    m_workerProcess->start();
+    m_workerProcess->write(m_server->address().toUtf8());
+    m_workerProcess->closeWriteChannel();
 }
 
-void PamAuthenticator::WorkerThread::run()
+void PamAuthenticator::connectWorker(const QDBusConnection &connection)
 {
-    QThread::run();
-    m_worker.reset();
+    Q_ASSERT(!m_dbusWorker); // only accept a connection once to mitigate the risk of a malicious actor connecting to us
+    qCDebug(KSCREENLOCKER_GREET) << "New D-Bus connection established" << connection.name();
+    auto c = connection; // make a non-const copy
+    c.registerObject(u"/org/kde/plasma/screenlocker"_s, this, QDBusConnection::ExportAdaptors);
+    m_dbusWorker = std::make_unique<OrgKdePlasmaScreenlockerWorkerInterface>(QString(), u"/org/kde/plasma/screenlocker/worker"_s, c);
+    m_dbusWorker->setTimeout(std::numeric_limits<int>::max()); // disable timeout, we expect blocking calls to arrive eventually
 }
diff --git a/greeter/pamauthenticator.h b/greeter/pamauthenticator.h
index aa562f60..a9c0de5e 100644
--- a/greeter/pamauthenticator.h
+++ b/greeter/pamauthenticator.h
@@ -7,14 +7,19 @@
 
 #pragma once
 
+#include <QDBusConnection>
+#include <QDBusContext>
+#include <QDBusMessage>
 #include <QDateTime>
 #include <QObject>
-#include <QThread>
+#include <memory>
 #include <qqmlregistration.h>
 
-class PamWorker;
+class QDBusServer;
+class QProcess;
+class OrgKdePlasmaScreenlockerWorkerInterface;
 
-class PamAuthenticator : public QObject
+class PamAuthenticator : public QObject, protected QDBusContext
 {
     Q_OBJECT
     QML_NAMED_ELEMENT(Authenticator)
@@ -102,9 +107,19 @@ public Q_SLOTS:
     void tryUnlock();
     void respond(const QByteArray &response);
     void cancel();
+    void Ping(const QString &message);
+    [[nodiscard]] QString Prompt(const QString &msg);
+    [[nodiscard]] QString MaskedPrompt(const QString &msg);
+    void StartFailedDelay(uint useconds);
+    void InfoMessage(const QString &msg);
+    void ErrorMessage(const QString &msg);
 
 private:
     void setBusy(bool busy);
+    [[nodiscard]] QString promptInternal(const QString &msg, bool isSecret);
+    void quitWorkerProcess();
+    void startWorker();
+    void connectWorker(const QDBusConnection &connection);
 
     const std::vector<std::pair<QMetaMethod, const QString &>> m_signalsToMembers;
     // NOTE Don't forget to reset in cancel as necessary
@@ -120,22 +135,11 @@ private:
     uint m_failedCount = 0;
     QDateTime m_lastFailed = QDateTime::currentDateTimeUtc();
     NoninteractiveAuthenticatorTypes m_authenticatorType;
-    // Tiny problem with bare bones QThread: when we shut down we want to clean up
-    // our subprocess correctly, but doing that means running a function on the thread
-    // before terminating it. This doesn't work out of the box because ther are
-    // no facilities to effectively invokeMethod while the QApplication is already
-    // mid shutdown. Instead we have a custom thread type that calls cleanup on the worker.
-    class WorkerThread : public QThread
-    {
-    public:
-        WorkerThread(std::unique_ptr<PamWorker> &&worker, QObject *parent = nullptr);
-        [[nodiscard]] PamWorker *worker() const;
-        void run() override;
-
-    private:
-        std::unique_ptr<PamWorker> m_worker;
-    } m_thread;
-    PamWorker *d;
+    QDBusMessage m_pendingPrompt;
+    QDBusServer *m_server = nullptr;
+    std::unique_ptr<OrgKdePlasmaScreenlockerWorkerInterface> m_dbusWorker;
+    QProcess *m_workerProcess = nullptr;
+    QString m_user;
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(PamAuthenticator::NoninteractiveAuthenticatorTypes)
diff --git a/greeter/pamworker.cpp b/greeter/pamworker.cpp
deleted file mode 100644
index 012e123a..00000000
--- a/greeter/pamworker.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
-// SPDX-FileCopyrightText: 2020 David Edmundson <[email protected]>
-// SPDX-FileCopyrightText: 2026 Harald Sitter <[email protected]>
-
-#include "pamworker.h"
-
-#include <QDBusConnection>
-#include <QDBusMessage>
-#include <QDBusPendingCall>
-#include <QDBusServer>
-#include <QProcess>
-#include <QTimer>
-
-#include <KLibexec>
-
-#include "kscreenlocker_greet_logging.h"
-#include "result.h"
-#include "screenlockeradaptor.h"
-
-using namespace std::chrono_literals;
-using namespace Qt::StringLiterals;
-
-PamWorker::PamWorker(const QString &service, const QString &user)
-    : QObject(nullptr)
-    , m_nextAttemptAllowedTime(std::chrono::steady_clock::now())
-    , m_service(service)
-    , m_username(user)
-{
-    connect(this, &PamWorker::promptResponseReceived, this, [this](const QByteArray &response) {
-        if (m_pendingPrompt.type() != QDBusMessage::InvalidMessage) {
-            QDBusMessage reply = m_pendingPrompt.createReply(QString::fromUtf8(response));
-            m_dbusWorker->connection().send(reply);
-            m_pendingPrompt = {};
-        } else {
-            qCWarning(KSCREENLOCKER_GREET) << "Received prompt response, but no pending prompt was found!";
-        }
-    });
-
-    connect(this, &PamWorker::cancelled, this, [this] {
-        if (!m_dbusWorker) {
-            return;
-        }
-        // Timings here are tight because we may need to cancel two workers, we don't want to get stuck on this for too long.
-        // Let it quit on its own. Mind that this usually will get stuck because the worker is itself waiting for a prompt response.
-        std::ignore = m_dbusWorker->Cancel();
-        if (m_workerProcess) {
-            m_workerProcess->waitForFinished((5ms).count());
-        }
-        // Make sure everything is cleaned up properly
-        quitWorkerProcess();
-    });
-}
-
-PamWorker::~PamWorker()
-{
-    quitWorkerProcess();
-}
-
-void PamWorker::authenticate()
-{
-    if (m_inAuthenticate) {
-        // No need to log in this case. We call authenticate multiple times to keep the auth session running even
-        // when the backend (e.g. fprint) aborts things.
-        return;
-    }
-
-    if (!m_dbusWorker) {
-        qCWarning(KSCREENLOCKER_GREET) << "DBus worker not initialized, cannot authenticate yet. Retry in a bit.";
-        return;
-    }
-
-    if (m_unavailable) {
-        qCDebug(KSCREENLOCKER_GREET) << "PAM service is not available. Cannot authenticate.";
-        return;
-    }
-
-    m_inAuthenticate = true;
-    Q_EMIT inAuthenticateChanged(m_inAuthenticate);
-    Q_EMIT busyChanged(true);
-    auto scopedAuthenticate = qScopeGuard([this] {
-        m_inAuthenticate = false;
-        Q_EMIT inAuthenticateChanged(m_inAuthenticate);
-        Q_EMIT busyChanged(false);
-    });
-
-    auto watcher = new QDBusPendingCallWatcher(m_dbusWorker->Authenticate(), this);
-    connect(watcher, &QDBusPendingCallWatcher::finished, this, [scopedAuthenticate = std::move(scopedAuthenticate), watcher, this]() {
-        watcher->deleteLater();
-        if (watcher->error().isValid()) {
-            qCWarning(KSCREENLOCKER_GREET) << "PAM worker failed to authenticate:" << watcher->error().message();
-            Q_EMIT failed();
-            return;
-        }
-
-        decltype(m_dbusWorker->Authenticate()) reply = watcher->reply();
-        switch (reply.value()) {
-        case WorkerResult::Type::Failure:
-            Q_EMIT failed();
-            return;
-        case WorkerResult::Type::Success:
-            Q_EMIT succeeded();
-            return;
-        case WorkerResult::Type::Unavailable:
-            m_unavailable = true;
-            Q_EMIT unavailabilityChanged(m_unavailable);
-            return;
-        };
-
-        qWarning() << "Unexpected authentication result" << reply.value();
-    });
-}
-
-void PamWorker::StartFailedDelay(uint useconds)
-{
-    m_nextAttemptAllowedTime = std::chrono::steady_clock::now() + std::chrono::microseconds(useconds);
-    Q_EMIT inPasswordDelayChanged(true);
-    QTimer::singleShot(std::chrono::microseconds(useconds), this, [this]() {
-        Q_EMIT inPasswordDelayChanged(false);
-    });
-    Q_EMIT loginFailedDelayStarted(useconds);
-}
-
-void PamWorker::InfoMessage(const QString &msg)
-{
-    Q_EMIT infoMessage(msg);
-}
-
-void PamWorker::ErrorMessage(const QString &msg)
-{
-    Q_EMIT errorMessage(msg);
-}
-
-void PamWorker::start()
-{
-    Q_ASSERT(!m_workerProcess);
-
-    qCDebug(KSCREENLOCKER_GREET) << "Starting PAM worker for service" << m_service << "and user" << m_username;
-    new ScreenlockerAdaptor(this);
-
-    auto server = new QDBusServer(this);
-    QObject::connect(server, &QDBusServer::newConnection, this, [this](const QDBusConnection &connection) {
-        Q_ASSERT(!m_dbusWorker); // only accept a connection once to mitigate the risk of a malicious actor connecting to us
-        qCDebug(KSCREENLOCKER_GREET) << "New D-Bus connection established" << connection.name();
-        auto c = connection; // make a non-const copy
-        c.registerObject(u"/org/kde/plasma/screenlocker"_s, this, QDBusConnection::ExportAdaptors);
-        m_dbusWorker = std::make_unique<org::kde::plasma::screenlocker::worker>(QString(), u"/org/kde/plasma/screenlocker/worker"_s, c);
-        m_dbusWorker->setTimeout(std::numeric_limits<int>::max()); // disable timeout, we expect blocking calls to arrive eventually
-    });
-
-    m_workerProcess = new QProcess(this);
-    m_workerProcess->setProcessChannelMode(QProcess::ForwardedChannels);
-    m_workerProcess->setProgram(KLibexec::path(u"kscreenlocker_worker"_s));
-    m_workerProcess->setArguments({m_service});
-    m_workerProcess->start();
-    m_workerProcess->write(server->address().toUtf8());
-    m_workerProcess->closeWriteChannel();
-}
-
-void PamWorker::Ping(const QString &message)
-{
-    qCDebug(KSCREENLOCKER_GREET) << "Ping received" << message << calledFromDBus();
-    org::kde::plasma::screenlocker::worker worker(QString(), u"/org/kde/plasma/screenlocker/worker"_s, connection());
-    auto watcher = new QDBusPendingCallWatcher(worker.Start(m_service, m_username), this);
-    connect(watcher, &QDBusPendingCallWatcher::finished, this, [watcher]() {
-        watcher->deleteLater();
-        Q_ASSERT(watcher->isValid());
-    });
-}
-
-QString PamWorker::Prompt(const QString &msg)
-{
-    return promptInternal(msg, false);
-}
-
-QString PamWorker::MaskedPrompt(const QString &msg)
-{
-    return promptInternal(msg, true);
-}
-
-QString PamWorker::promptInternal(const QString &msg, bool isSecret)
-{
-    Q_EMIT busyChanged(false);
-
-    if (isSecret) {
-        Q_EMIT promptForSecret(msg);
-    } else {
-        Q_EMIT prompt(msg);
-    }
-
-    qCDebug(KSCREENLOCKER_GREET,
-            "[PAM worker %s] Message: %s: %s",
-            qUtf8Printable(m_service),
-            (isSecret ? "Echo-off prompt" : "Echo-on prompt"),
-            qUtf8Printable(msg));
-
-    setDelayedReply(true);
-
-    m_pendingPrompt = message();
-
-    return {};
-}
-
-void PamWorker::quitWorkerProcess()
-{
-    if (m_workerProcess) {
-        m_workerProcess->terminate();
-        if (!m_workerProcess->waitForFinished((25ms).count())) {
-            qWarning() << "Worker did not terminate in time, killing it.";
-            m_workerProcess->kill();
-        }
-    }
-    m_dbusWorker.reset();
-    m_workerProcess = nullptr;
-}
diff --git a/greeter/pamworker.h b/greeter/pamworker.h
deleted file mode 100644
index ed2a9e71..00000000
--- a/greeter/pamworker.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
-// SPDX-FileCopyrightText: 2020 David Edmundson <[email protected]>
-// SPDX-FileCopyrightText: 2026 Harald Sitter <[email protected]>
-
-#pragma once
-
-#include <memory>
-
-#include <QDBusContext>
-#include <QDBusMessage>
-#include <QObject>
-
-#include "org.kde.plasma.screenlocker.worker.h"
-
-class QProcess;
-
-class PamWorker : public QObject, QDBusContext
-{
-    Q_OBJECT
-public:
-    PamWorker(const QString &service, const QString &user);
-    ~PamWorker() override;
-    Q_DISABLE_COPY_MOVE(PamWorker)
-    void start();
-    void authenticate();
-    void StartFailedDelay(uint useconds);
-    void InfoMessage(const QString &msg);
-    void ErrorMessage(const QString &msg);
-
-public Q_SLOTS:
-    void Ping(const QString &message);
-    [[nodiscard]] QString Prompt(const QString &msg);
-    [[nodiscard]] QString MaskedPrompt(const QString &msg);
-
-Q_SIGNALS:
-    void busyChanged(bool busy);
-    void promptForSecret(const QString &msg);
-    void prompt(const QString &msg);
-    void infoMessage(const QString &msg);
-    void errorMessage(const QString &msg);
-    void failed();
-    void loginFailedDelayStarted(const uint uSecDelay);
-    void succeeded();
-    void unavailabilityChanged(bool unavailable);
-    void inAuthenticateChanged(bool inAuthenticate);
-    void inPasswordDelayChanged(bool timeout);
-
-    // internal
-    void promptResponseReceived(const QByteArray &prompt);
-    void cancelled();
-
-private:
-    [[nodiscard]] QString promptInternal(const QString &msg, bool isSecret);
-    void quitWorkerProcess();
-
-    bool m_unavailable = false;
-    bool m_inAuthenticate = false;
-    std::chrono::steady_clock::time_point m_nextAttemptAllowedTime;
-    int m_result = -1;
-    QString m_service;
-    QString m_username;
-    QDBusMessage m_pendingPrompt;
-    std::unique_ptr<org::kde::plasma::screenlocker::worker> m_dbusWorker;
-    QProcess *m_workerProcess = nullptr;
-};
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.