From b5a9f5f6f5f618fbc76a3622ef684fa22db08a29 Mon Sep 17 00:00:00 2001 From: April & May & June Date: Thu, 18 Dec 2025 13:59:42 +0800 Subject: [PATCH] refactor: cut off helper - Merge HelperApp into Auth - Move Auth & UserSession into daemon - Simplify Auth, remove redundant logics This commit largely simplify the code. Note that the function of starting sessions other than treeland will temporarily not work after this commit, due to some wrongly designed obsolete code. This will be fixed soon in the next commit. --- REUSE.toml | 2 +- debian/ddm.install | 1 - src/CMakeLists.txt | 2 - src/auth/Auth.cpp | 508 ------------------------- src/auth/Auth.h | 275 ------------- src/auth/AuthMessages.h | 215 ----------- src/auth/AuthPrompt.cpp | 72 ---- src/auth/AuthPrompt.h | 102 ----- src/auth/AuthRequest.cpp | 111 ------ src/auth/AuthRequest.h | 94 ----- src/auth/CMakeLists.txt | 63 --- src/daemon/Auth.cpp | 202 ++++++++++ src/daemon/Auth.h | 128 +++++++ src/daemon/CMakeLists.txt | 6 +- src/daemon/Display.cpp | 142 +++---- src/daemon/Display.h | 5 +- src/daemon/Greeter.cpp | 66 +--- src/daemon/Greeter.h | 6 +- src/{helper => daemon}/Pam.cpp | 0 src/{helper => daemon}/Pam.h | 0 src/{helper => daemon}/UserSession.cpp | 170 +++------ src/{helper => daemon}/UserSession.h | 37 +- src/helper/CMakeLists.txt | 81 ---- src/helper/HelperApp.cpp | 451 ---------------------- src/helper/HelperApp.h | 93 ----- src/helper/HelperStartSingleMode.cpp | 50 --- src/helper/HelperStartWayland.cpp | 77 ---- src/helper/HelperStartX11User.cpp | 76 ---- src/helper/singlewaylandhelper.cpp | 44 --- src/helper/singlewaylandhelper.h | 17 - src/helper/waylandhelper.cpp | 141 ------- src/helper/waylandhelper.h | 54 --- src/helper/waylandsocketwatcher.cpp | 107 ------ src/helper/waylandsocketwatcher.h | 64 ---- src/helper/xorguserhelper.cpp | 241 ------------ src/helper/xorguserhelper.h | 61 --- 36 files changed, 457 insertions(+), 3307 deletions(-) delete mode 100644 src/auth/Auth.cpp delete mode 100644 src/auth/Auth.h delete mode 100644 src/auth/AuthMessages.h delete mode 100644 src/auth/AuthPrompt.cpp delete mode 100644 src/auth/AuthPrompt.h delete mode 100644 src/auth/AuthRequest.cpp delete mode 100644 src/auth/AuthRequest.h delete mode 100644 src/auth/CMakeLists.txt create mode 100644 src/daemon/Auth.cpp create mode 100644 src/daemon/Auth.h rename src/{helper => daemon}/Pam.cpp (100%) rename src/{helper => daemon}/Pam.h (100%) rename src/{helper => daemon}/UserSession.cpp (71%) rename src/{helper => daemon}/UserSession.h (65%) delete mode 100644 src/helper/CMakeLists.txt delete mode 100644 src/helper/HelperApp.cpp delete mode 100644 src/helper/HelperApp.h delete mode 100644 src/helper/HelperStartSingleMode.cpp delete mode 100644 src/helper/HelperStartWayland.cpp delete mode 100644 src/helper/HelperStartX11User.cpp delete mode 100644 src/helper/singlewaylandhelper.cpp delete mode 100644 src/helper/singlewaylandhelper.h delete mode 100644 src/helper/waylandhelper.cpp delete mode 100644 src/helper/waylandhelper.h delete mode 100644 src/helper/waylandsocketwatcher.cpp delete mode 100644 src/helper/waylandsocketwatcher.h delete mode 100644 src/helper/xorguserhelper.cpp delete mode 100644 src/helper/xorguserhelper.h diff --git a/REUSE.toml b/REUSE.toml index 6b95a98..f3b1e7c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -69,7 +69,7 @@ SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" [[annotations]] -path = ["releng/prepare-relnotes", "src/auth/Auth.cpp", "src/auth/Auth.h", "src/auth/AuthMessages.h", "src/auth/AuthPrompt.cpp", "src/auth/AuthPrompt.h", "src/auth/AuthRequest.cpp", "src/auth/AuthRequest.h", "src/common/ConfigReader.cpp", "src/common/ConfigReader.h", "src/common/Configuration.cpp", "src/common/Configuration.h", "src/common/MessageHandler.h", "src/common/Messages.h", "src/common/SafeDataStream.cpp", "src/common/SafeDataStream.h", "src/common/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.h", "src/common/ThemeConfig.cpp", "src/common/ThemeConfig.h", "src/common/ThemeMetadata.cpp", "src/common/ThemeMetadata.h", "src/common/VirtualTerminal.cpp", "src/common/VirtualTerminal.h", "src/common/XAuth.cpp", "src/common/XAuth.h", "src/daemon/DaemonApp.cpp", "src/daemon/DaemonApp.h", "src/daemon/Display.cpp", "src/daemon/Display.h", "src/daemon/DisplayManager.cpp", "src/daemon/DisplayManager.h", "src/daemon/DisplayServer.cpp", "src/daemon/DisplayServer.h", "src/daemon/Greeter.cpp", "src/daemon/Greeter.h", "src/daemon/PowerManager.cpp", "src/daemon/PowerManager.h", "src/daemon/Seat.cpp", "src/daemon/Seat.h", "src/daemon/SeatManager.cpp", "src/daemon/SeatManager.h", "src/daemon/SocketServer.cpp", "src/daemon/SocketServer.h", "src/daemon/TreelandConnector.cpp", "src/daemon/TreelandConnector.h", "src/daemon/Utils.h", "src/daemon/WaylandDisplayServer.cpp", "src/daemon/WaylandDisplayServer.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/XorgUserDisplayServer.cpp", "src/daemon/XorgUserDisplayServer.h", "src/greeter/GreeterApp.h", "src/greeter/GreeterProxy.cpp", "src/greeter/GreeterProxy.h", "src/greeter/SessionModel.cpp", "src/greeter/SessionModel.h", "src/greeter/UserModel.cpp", "src/greeter/UserModel.h", "src/helper/Pam.cpp", "src/helper/Pam.h", "src/helper/HelperApp.cpp", "src/helper/HelperApp.h", "src/helper/HelperStartWayland.cpp", "src/helper/HelperStartX11User.cpp", "src/helper/UserSession.cpp", "src/helper/UserSession.h", "src/helper/waylandhelper.cpp", "src/helper/waylandhelper.h", "src/helper/waylandsocketwatcher.cpp", "src/helper/waylandsocketwatcher.h", "src/helper/xorguserhelper.cpp", "src/helper/xorguserhelper.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h", "src/greeter/GreeterApp.cpp"] +path = ["releng/prepare-relnotes", "src/daemon/Auth.cpp", "src/daemon/Auth.h", "src/common/ConfigReader.cpp", "src/common/ConfigReader.h", "src/common/Configuration.cpp", "src/common/Configuration.h", "src/common/MessageHandler.h", "src/common/Messages.h", "src/common/SafeDataStream.cpp", "src/common/SafeDataStream.h", "src/common/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.h", "src/common/ThemeConfig.cpp", "src/common/ThemeConfig.h", "src/common/ThemeMetadata.cpp", "src/common/ThemeMetadata.h", "src/common/VirtualTerminal.cpp", "src/common/VirtualTerminal.h", "src/common/XAuth.cpp", "src/common/XAuth.h", "src/daemon/DaemonApp.cpp", "src/daemon/DaemonApp.h", "src/daemon/Display.cpp", "src/daemon/Display.h", "src/daemon/DisplayManager.cpp", "src/daemon/DisplayManager.h", "src/daemon/DisplayServer.cpp", "src/daemon/DisplayServer.h", "src/daemon/Greeter.cpp", "src/daemon/Greeter.h", "src/daemon/PowerManager.cpp", "src/daemon/PowerManager.h", "src/daemon/Seat.cpp", "src/daemon/Seat.h", "src/daemon/SeatManager.cpp", "src/daemon/SeatManager.h", "src/daemon/SocketServer.cpp", "src/daemon/SocketServer.h", "src/daemon/TreelandConnector.cpp", "src/daemon/TreelandConnector.h", "src/daemon/Utils.h", "src/daemon/WaylandDisplayServer.cpp", "src/daemon/WaylandDisplayServer.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/XorgUserDisplayServer.cpp", "src/daemon/XorgUserDisplayServer.h", "src/greeter/GreeterApp.h", "src/greeter/GreeterProxy.cpp", "src/greeter/GreeterProxy.h", "src/greeter/SessionModel.cpp", "src/greeter/SessionModel.h", "src/greeter/UserModel.cpp", "src/greeter/UserModel.h", "src/daemon/Pam.cpp", "src/daemon/Pam.h", "src/daemon/UserSession.cpp", "src/daemon/UserSession.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h", "src/greeter/GreeterApp.cpp"] precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" diff --git a/debian/ddm.install b/debian/ddm.install index d6a843e..33ae3be 100644 --- a/debian/ddm.install +++ b/debian/ddm.install @@ -1,5 +1,4 @@ usr/bin/* -usr/libexec/* usr/share/* usr/lib/systemd/* usr/lib/tmpfiles.d/* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2c4925..e6fb938 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,4 @@ if (QT_KNOWN_POLICY_QTP0001) endif() add_subdirectory(common) -add_subdirectory(auth) add_subdirectory(daemon) -add_subdirectory(helper) diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp deleted file mode 100644 index 50ad8f1..0000000 --- a/src/auth/Auth.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "Auth.h" -#include "Constants.h" -#include "AuthMessages.h" -#include "SafeDataStream.h" - -#include -#include -#include -#include - -#include - -#include - -#include -#include - -namespace DDM { - class Auth::SocketServer : public QLocalServer { - Q_OBJECT - public slots: - void handleNewConnection(); - public: - static SocketServer *instance(); - - QMap helpers; - private: - SocketServer(); - }; - - class Auth::Private : public QObject { - Q_OBJECT - public: - Private(Auth *parent); - ~Private(); - void setSocket(QLocalSocket *socket); - public slots: - void dataPending(); - void childExited(int exitCode, QProcess::ExitStatus exitStatus); - void childError(QProcess::ProcessError error); - void requestFinished(); - public: - AuthRequest *request { nullptr }; - QProcess *child { nullptr }; - QLocalSocket *socket { nullptr }; - QString displayServerCmd; - QString sessionPath { }; - Session::Type sessionType { Session::UnknownSession }; - QString sessionFileName { }; - QString user { }; - QString password { }; - QByteArray cookie { }; - bool autologin { false }; - bool greeter { false }; - bool singleMode { false }; - bool identifyOnly { false }; - bool skipAuth { false }; - QProcessEnvironment environment { }; - qint64 id { 0 }; - static qint64 lastId; - QString sessionId; - int tty { 0 }; - int xdgSessionId { 0 }; - }; - - qint64 Auth::Private::lastId = 1; - - - - Auth::SocketServer::SocketServer() - : QLocalServer() { - connect(this, &QLocalServer::newConnection, this, &Auth::SocketServer::handleNewConnection); - } - - void Auth::SocketServer::handleNewConnection() { - while (hasPendingConnections()) { - Msg m = Msg::MSG_UNKNOWN; - qint64 id; - QLocalSocket *socket = nextPendingConnection(); - SafeDataStream str(socket); - str.receive(); - str >> m >> id; - if (m == Msg::HELLO && id && SocketServer::instance()->helpers.contains(id)) { - helpers[id]->setSocket(socket); - if (socket->bytesAvailable() > 0) - helpers[id]->dataPending(); - } - } - } - - Auth::SocketServer* Auth::SocketServer::instance() { - static std::unique_ptr self; - if (!self) { - self.reset(new SocketServer()); - self->listen(QStringLiteral("ddm-auth-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces))); - } - return self.get(); - } - - - Auth::Private::Private(Auth *parent) - : QObject(parent) - , request(new AuthRequest(parent)) - , child(new QProcess(this)) - , id(lastId++) { - SocketServer::instance()->helpers[id] = this; - QProcessEnvironment env = child->processEnvironment(); - bool langEmpty = true; - QFile localeFile(QStringLiteral("/etc/locale.conf")); - if (localeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&localeFile); - while (!in.atEnd()) { - QStringList parts = in.readLine().split(QLatin1Char('=')); - if (parts.size() >= 2) { - env.insert(parts[0], parts[1]); - if (parts[0] == QLatin1String("LANG")) - langEmpty = false; - } - } - localeFile.close(); - } - if (langEmpty) - env.insert(QStringLiteral("LANG"), QStringLiteral("C")); - child->setProcessEnvironment(env); - connect(child, QOverload::of(&QProcess::finished), this, &Auth::Private::childExited); - connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError); - connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished); - connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged); - } - - Auth::Private::~Private() - { - SocketServer::instance()->helpers.remove(id); - } - - - void Auth::Private::setSocket(QLocalSocket *socket) { - this->socket = socket; - connect(socket, &QLocalSocket::readyRead, this, &Auth::Private::dataPending); - } - - void Auth::Private::dataPending() { - Auth *auth = qobject_cast(parent()); - Msg m = MSG_UNKNOWN; - SafeDataStream str(socket); - while (socket->bytesAvailable() > 0) { - str.receive(); - str >> m; - switch (m) { - case ERROR: { - QString message; - Error type = ERROR_NONE; - str >> message >> type; - Q_EMIT auth->error(message, type); - break; - } - case INFO: { - QString message; - Info type = INFO_NONE; - str >> message >> type; - Q_EMIT auth->info(message, type); - break; - } - case REQUEST: { - Request r; - str >> r; - request->setRequest(&r); - break; - } - case AUTHENTICATED: { - QString user; - str >> user; - if (!user.isEmpty()) { - auth->setUser(user); - Q_EMIT auth->authentication(user, true, auth->identifyOnly()); - str.reset(); - str << AUTHENTICATED << environment << cookie; - str.send(); - } - else { - Q_EMIT auth->authentication(auth->user(), false, auth->identifyOnly()); - } - break; - } - case SESSION_STATUS: { - bool status; - int sessionId; - str >> status >> sessionId; - if(!auth->identifyOnly()) { - Q_EMIT auth->sessionStarted(status, sessionId); - } - str.reset(); - str << SESSION_STATUS; - str.send(); - break; - } - case DISPLAY_SERVER_STARTED: { - QString displayName; - str >> displayName; - Q_EMIT auth->displayServerReady(displayName); - str.reset(); - str << DISPLAY_SERVER_STARTED; - str.send(); - break; - } - default: { - Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); - } - } - } - } - - void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) { - if (exitStatus != QProcess::NormalExit) { - qWarning("Auth: ddm-helper (%s) crashed (exit code %d)", - qPrintable(child->arguments().join(QLatin1Char(' '))), - HelperExitStatus(exitStatus)); - Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); - } - - if (exitCode == HELPER_SUCCESS) - qDebug() << "Auth: ddm-helper exited successfully"; - else - qWarning("Auth: ddm-helper exited with %d", exitCode); - - Q_EMIT qobject_cast(parent())->finished((Auth::HelperExitStatus)exitCode); - } - - void Auth::Private::childError(QProcess::ProcessError error) { - Q_UNUSED(error); - Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); - } - - void Auth::Private::requestFinished() { - SafeDataStream str(socket); - Request r = request->request(); - str << REQUEST << r; - str.send(); - request->setRequest(); - } - - - Auth::Auth(const QString &user, const QString &session, bool autologin, QObject *parent, bool verbose) - : QObject(parent) - , d(new Private(this)) { - setUser(user); - setAutologin(autologin); - setSession(session); - setVerbose(verbose); - } - - Auth::Auth(QObject* parent) - : QObject(parent) - , d(new Private(this)) { - } - - Auth::~Auth() { - stop(); - delete d; - } - - void Auth::registerTypes() { - qmlRegisterAnonymousType("Auth", 1); - qmlRegisterAnonymousType("Auth", 1); - qmlRegisterType("Auth", 1, 0, "Auth"); - } - - bool Auth::autologin() const { - return d->autologin; - } - - bool Auth::isGreeter() const - { - return d->greeter; - } - - const QByteArray& Auth::cookie() const { - return d->cookie; - } - - const QString &Auth::session() const { - return d->sessionPath; - } - - const QString &Auth::password() const { - return d->password; - } - - const QString &Auth::user() const { - return d->user; - } - - bool Auth::verbose() const { - return d->child->processChannelMode() == QProcess::ForwardedChannels; - } - - AuthRequest *Auth::request() { - return d->request; - } - - QString Auth::sessionId() const { - return d->sessionId; - } - - Session::Type Auth::sessionType() const { - return d->sessionType; - } - - QString Auth::sessionFileName() const { - return d->sessionFileName; - } - - bool Auth::isSingleMode() const { - return d->singleMode; - } - - bool Auth::isActive() const { - return d->child->state() != QProcess::NotRunning; - } - - void Auth::insertEnvironment(const QProcessEnvironment &env) { - d->environment.insert(env); - } - - void Auth::insertEnvironment(const QString &key, const QString &value) { - d->environment.insert(key, value); - } - - void Auth::setCookie(const QByteArray& cookie) { - if (cookie != d->cookie) { - d->cookie = cookie; - Q_EMIT cookieChanged(); - } - } - - void Auth::setUser(const QString &user) { - if (user != d->user) { - d->user = user; - Q_EMIT userChanged(); - } - } - - void Auth::setPassword(const QString &password) { - if (password != d->password) { - d->password = password; - } - } - - void Auth::setAutologin(bool on) { - if (on != d->autologin) { - d->autologin = on; - Q_EMIT autologinChanged(); - } - } - - void Auth::setGreeter(bool on) - { - if (on != d->greeter) { - d->greeter = on; - Q_EMIT greeterChanged(); - } - } - - void Auth::setDisplayServerCommand(const QString &command) - { - if (d->displayServerCmd != command) { - d->displayServerCmd = command; - Q_EMIT displayServerCommandChanged(); - } - } - - void Auth::setSession(const QString& path) { - if (path != d->sessionPath) { - d->sessionPath = path; - Q_EMIT sessionChanged(); - } - } - - void Auth::setSingleMode(bool on) - { - if (on != d->singleMode) { - d->singleMode = on; - Q_EMIT singleModeChanged(); - } - } - - void Auth::setSessionId(const QString& sessionId) - { - if (sessionId != d->sessionId) { - d->sessionId = sessionId; - } - } - - void Auth::setSessionType(const Session::Type type) { - d->sessionType = type; - } - - void Auth::setSessionFileName(const QString &fileName) { - d->sessionFileName = fileName; - } - - int Auth::tty() const { - return d->tty; - } - - void Auth::setTTY(int tty) { - if (tty != d->tty) { - d->tty = tty; - } - } - - int Auth::xdgSessionId() const { - return d->xdgSessionId; - } - - void Auth::setXdgSessionId(int xdgSessionId) { - d->xdgSessionId = xdgSessionId; - } - - void Auth::setVerbose(bool on) { - if (on != verbose()) { - if (on) - d->child->setProcessChannelMode(QProcess::ForwardedChannels); - else - d->child->setProcessChannelMode(QProcess::SeparateChannels); - Q_EMIT verboseChanged(); - } - } - - bool Auth::identifyOnly() const { - return d->identifyOnly; - } - - void Auth::setIdentifyOnly(bool on) { - if (on != d->identifyOnly) { - d->identifyOnly = on; - } - } - - void Auth::setSkipAuth(bool on) { - if (on != d->skipAuth) { - d->skipAuth = on; - } - } - - void Auth::start() { - QStringList args; - args << QStringLiteral("--socket") << SocketServer::instance()->fullServerName(); - args << QStringLiteral("--id") << QString::number(d->id); - if (!d->sessionPath.isEmpty()) - args << QStringLiteral("--start") << d->sessionPath; - if (!d->user.isEmpty()) - args << QStringLiteral("--user") << d->user; - if (d->autologin) - args << QStringLiteral("--autologin"); - if (!d->displayServerCmd.isEmpty()) - args << QStringLiteral("--display-server") << d->displayServerCmd; - if (d->greeter) - args << QStringLiteral("--greeter"); - if (d->singleMode) - args << QStringLiteral("--single-mode"); - if (d->identifyOnly) - args << QStringLiteral("--identify-only"); - if (d->skipAuth) - args << QStringLiteral("--skip-auth"); - d->child->start(QStringLiteral("%1/ddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); - } - - void Auth::stop() { - if (d->child->state() == QProcess::NotRunning) { - return; - } - - d->child->terminate(); - - // wait for finished - // TODO: Cut off the waiting. - // The code will be executed when user trying to start sessions other - // than treeland, which will stop the currentAuth and start a new one. - // This process involves the removal of seatd client, which needs a - // small amount of time to wait. Consider to make this process under - // control. - if (!d->child->waitForFinished(500)) - d->child->kill(); - } -} - -#include "Auth.moc" diff --git a/src/auth/Auth.h b/src/auth/Auth.h deleted file mode 100644 index 79ef928..0000000 --- a/src/auth/Auth.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Qt Authentication library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef DDM_AUTH_H -#define DDM_AUTH_H - -#include "AuthRequest.h" -#include "AuthPrompt.h" -#include "Session.h" - -#include -#include - -namespace DDM { - /** - * \brief - * Main class triggering the authentication and handling all communication - * - * \section description - * There are three basic kinds of authentication: - * - * * Checking only the validity of the user's secrets - The default values - * - * * Logging the user in after authenticating him - You'll have to set the - * \ref session property to do that. - * - * * Logging the user in without authenticating - You'll have to set the - * \ref session and \ref autologin properties to do that. - * - * Usage: - * - * Just construct, connect the signals (especially \ref requestChanged) - * and fire up \ref start - */ - class Auth : public QObject { - Q_OBJECT - // not setting NOTIFY for the properties - they should be set only once before calling start - Q_PROPERTY(bool autologin READ autologin WRITE setAutologin NOTIFY autologinChanged) - Q_PROPERTY(bool greeter READ isGreeter WRITE setGreeter NOTIFY greeterChanged) - Q_PROPERTY(bool verbose READ verbose WRITE setVerbose NOTIFY verboseChanged) - Q_PROPERTY(bool identifyOnly READ identifyOnly WRITE setIdentifyOnly) - Q_PROPERTY(QByteArray cookie READ cookie WRITE setCookie NOTIFY cookieChanged) - Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged) - Q_PROPERTY(QString session READ session WRITE setSession NOTIFY sessionChanged) - Q_PROPERTY(AuthRequest* request READ request NOTIFY requestChanged) - public: - explicit Auth(const QString &user = QString(), const QString &session = QString(), bool autologin = false, QObject *parent = 0, bool verbose = false); - explicit Auth(QObject *parent); - ~Auth(); - - enum Info { - INFO_NONE = 0, - INFO_UNKNOWN, - INFO_PASS_CHANGE_REQUIRED, - _INFO_LAST - }; - Q_ENUM(Info) - - enum Error { - ERROR_NONE = 0, - ERROR_UNKNOWN, - ERROR_AUTHENTICATION, - ERROR_INTERNAL, - _ERROR_LAST - }; - Q_ENUM(Error) - - enum HelperExitStatus { - HELPER_SUCCESS = 0, - HELPER_AUTH_ERROR, - HELPER_SESSION_ERROR, - HELPER_OTHER_ERROR, - HELPER_DISPLAYSERVER_ERROR, - HELPER_TTY_ERROR, - }; - Q_ENUM(HelperExitStatus) - - static void registerTypes(); - - bool autologin() const; - bool isGreeter() const; - bool verbose() const; - bool identifyOnly() const; - bool isSingleMode() const; - const QByteArray &cookie() const; - const QString &user() const; - const QString &session() const; - const QString &password() const; - AuthRequest *request(); - QString sessionId() const; - Session::Type sessionType() const; - QString sessionFileName() const; - int tty() const; - int xdgSessionId() const; - - void setTTY(int tty); - /** - * True if an authentication or session is in progress - */ - bool isActive() const; - - /** - * If starting a session, you will probably want to provide some basic env variables for the session. - * This only inserts the variables - if the current key already had a value, it will be overwritten. - * User-specific data such as $HOME is generated automatically. - * @param env the environment - */ - void insertEnvironment(const QProcessEnvironment &env); - - /** - * Works the same as \ref insertEnvironment but only for one key-value pair - * @param key key - * @param value value - */ - void insertEnvironment(const QString &key, const QString &value); - - /** - * Set mode to autologin. - * Ignored if session is not started - * @param on true if should autologin - */ - void setAutologin(bool on = true); - - /** - * Set mode to greeter - * This will bypass authentication checks - */ - void setGreeter(bool on = true); - - /** - * Forwards the output of the underlying authenticator to the current process - * @param on true if should forward the output - */ - void setVerbose(bool on = true); - - void setIdentifyOnly(bool on = false); - - void setSkipAuth(bool on = true); - /** - * Sets the user which will then authenticate - * @param user username - */ - void setUser(const QString &user); - - - void setPassword(const QString &password); - - /** - * Set the display server command to be started before the greeter. - * @param command Command of the display server to be started - */ - void setDisplayServerCommand(const QString &command); - - /** - * Set the session to be started after authenticating. - * @param path Path of the session executable to be started - */ - void setSession(const QString &path); - - /** - * Set the display server cookie, to be inserted into the user's $XAUTHORITY - * @param cookie cookie data - */ - void setCookie(const QByteArray &cookie); - - /** - * Set the display server single mode - * @param on true use DDE single wayland mode - */ - void setSingleMode(bool on = true); - - void setSessionId(const QString &sessionId); - - void setSessionType(const Session::Type type); - - void setSessionFileName(const QString &fileName); - - void setXdgSessionId(int xdgSessionId); - - public Q_SLOTS: - /** - * Sets up the environment and starts the authentication - */ - void start(); - - /** - * Indicates that we do not need the process anymore. - */ - void stop(); - - Q_SIGNALS: - void autologinChanged(); - void greeterChanged(); - void verboseChanged(); - void cookieChanged(); - void userChanged(); - void displayServerCommandChanged(); - void sessionChanged(); - void requestChanged(); - void singleModeChanged(); - - /** - * Emitted when authentication phase finishes - * - * @note If you want to set some environment variables for the session right before the - * session is started, connect to this signal using a blocking connection and insert anything - * you need in the slot. - * @param user username - * @param success true if succeeded - */ - void authentication(QString user, bool success, bool identifyOnly); - - /** - * Emitted when session starting phase finishes - * - * @param success true if succeeded - */ - void sessionStarted(bool success, int xdgSessionId); - - /** - * Emitted when the display server is ready. - * - * @param displayName display name - */ - void displayServerReady(const QString &displayName); - - /** - * Emitted when the helper quits, either after authentication or when the session ends. - * Or, when something goes wrong. - * - * @param success true if every underlying task went fine - */ - void finished(Auth::HelperExitStatus status); - - /** - * Emitted on error - * - * @param message message to be displayed to the user - */ - void error(QString message, Auth::Error type); - - /** - * Information from the underlying stack is to be presented to the user - * - * @param message message to be displayed to the user - */ - void info(QString message, Auth::Info type); - - private: - class Private; - class SocketServer; - friend Private; - friend SocketServer; - Private *d { nullptr }; - }; -} - -#endif // DDM_AUTH_H diff --git a/src/auth/AuthMessages.h b/src/auth/AuthMessages.h deleted file mode 100644 index 98083cb..0000000 --- a/src/auth/AuthMessages.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Message IDs to pass between the library and the helper - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef MESSAGES_H -#define MESSAGES_H - -#include -#include - -#include "Auth.h" - -namespace DDM { - class Prompt { - public: - Prompt() { } - Prompt(AuthPrompt::Type type, QString message, bool hidden) - : type(type), message(message), hidden(hidden) { } - Prompt(const Prompt &o) - : type(o.type), response(o.response), message(o.message), hidden(o.hidden) { } - ~Prompt() { - clear(); - } - Prompt& operator=(const Prompt &o) { - type = o.type; - response = o.response; - message = o.message; - hidden = o.hidden; - return *this; - } - bool operator==(const Prompt &o) const { - return type == o.type && response == o.response && message == o.message && hidden == o.hidden; - } - bool valid() const { - return !(type == AuthPrompt::NONE && response.isEmpty() && message.isEmpty()); - } - void clear() { - type = AuthPrompt::NONE; - // overwrite the whole thing with zeroes before clearing - memset(response.data(), 0, response.length()); - response.clear(); - message.clear(); - hidden = false; - } - - AuthPrompt::Type type { AuthPrompt::NONE }; - QByteArray response { }; - QString message { }; - bool hidden { false }; - }; - - class Request { - public: - Request() { } - Request(QList prompts) - : prompts(prompts) { } - Request(const Request &o) - : prompts(o.prompts) { } - Request& operator=(const Request &o) { - prompts = QList(o.prompts); - return *this; - } - bool operator==(const Request &o) const { - return prompts == o.prompts; - } - bool valid() const { - return !(prompts.isEmpty()); - } - void clear() { - prompts.clear(); - } - - QList prompts { }; - }; - - enum Msg { - MSG_UNKNOWN = 0, - HELLO = 1, - ERROR, - INFO, - REQUEST, - AUTHENTICATED, - SESSION_STATUS, - DISPLAY_SERVER_STARTED, - MSG_LAST, - }; - - inline QDataStream& operator<<(QDataStream &s, const Msg &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Msg &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= MSG_LAST || i <= MSG_UNKNOWN) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Msg(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Auth::Error &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Auth::Error &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= Auth::_ERROR_LAST || i < Auth::ERROR_NONE) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Auth::Error(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Auth::Info &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Auth::Info &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= Auth::_INFO_LAST || i < Auth::INFO_NONE) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Auth::Info(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const QProcessEnvironment &m) { - s << m.toStringList(); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, QProcessEnvironment &m) { - QStringList l; - s >> l; - for (QString s : l) { - int pos = s.indexOf(QLatin1Char('=')); - m.insert(s.left(pos), s.mid(pos + 1)); - } - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Prompt &m) { - s << qint32(m.type) << m.message << m.hidden << m.response; - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Prompt &m) { - qint32 type; - QString message; - bool hidden; - QByteArray response; - s >> type >> message >> hidden >> response; - m.type = AuthPrompt::Type(type); - m.message = message; - m.hidden = hidden; - m.response = response; - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Request &m) { - qint32 length = m.prompts.length(); - s << length; - for (const Prompt &p : std::as_const(m.prompts)) { - s << p; - } - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Request &m) { - QList prompts; - qint32 length; - s >> length; - for (int i = 0; i < length; i++) { - Prompt p; - s >> p; - prompts << p; - } - if (prompts.length() != length) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m.prompts = prompts; - return s; - } -} - -#endif // MESSAGES_H diff --git a/src/auth/AuthPrompt.cpp b/src/auth/AuthPrompt.cpp deleted file mode 100644 index 0c4d36e..0000000 --- a/src/auth/AuthPrompt.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "AuthPrompt.h" -#include "Auth.h" -#include "AuthMessages.h" - -namespace DDM { - class AuthPrompt::Private : public Prompt { - public: - Private(const Prompt *p) { - // initializers are too mainstream i guess - type = p->type; - hidden = p->hidden; - message = p->message; - response = p->response; - } - }; - - AuthPrompt::AuthPrompt(const Prompt *prompt, AuthRequest *parent) - : QObject(parent) - , d(new Private(prompt)) { - } - - AuthPrompt::~AuthPrompt() { - delete d; - } - - AuthPrompt::Type AuthPrompt::type() const { - return d->type; - } - - QString AuthPrompt::message() const { - return d->message; - } - - QByteArray AuthPrompt::response() const { - return d->response; - } - - QByteArray AuthPrompt::responseFake() { - return QByteArray(); - } - - void AuthPrompt::setResponse(const QByteArray &r) { - if (r != d->response) { - d->response = r; - Q_EMIT responseChanged(); - } - } - - bool AuthPrompt::hidden() const { - return d->hidden; - } -} diff --git a/src/auth/AuthPrompt.h b/src/auth/AuthPrompt.h deleted file mode 100644 index dd3205c..0000000 --- a/src/auth/AuthPrompt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef PROMPT_H -#define PROMPT_H - -#include - -namespace DDM { - class Auth; - class AuthRequest; - class Prompt; - /** - * \brief - * One prompt input for the authentication - * - * \section description - * The main, not completely obvious rationale for this class is: - * - * \warning Don't use the \ref message property if you have your own strings for - * the \ref Type -s. PAM sends horrible horrible stuff and passwd obviously - * doesn't tell us a thing. - */ - class AuthPrompt : public QObject { - Q_OBJECT - Q_PROPERTY(Type type READ type CONSTANT) - Q_PROPERTY(QString message READ message CONSTANT) - Q_PROPERTY(bool hidden READ hidden CONSTANT) - Q_PROPERTY(QByteArray response READ responseFake WRITE setResponse NOTIFY responseChanged) - public: - virtual ~AuthPrompt(); - /** - * \note In hex not for binary operations but to leave space for adding other codes - */ - enum Type { - NONE = 0x0000, ///< No type - UNKNOWN = 0x0001, ///< Unknown type - CHANGE_CURRENT = 0x0010, ///< On changing the password: Current one - CHANGE_NEW, ///< On changing the password: The new one - CHANGE_REPEAT, ///< On changing the password: The new one, repeated - LOGIN_USER = 0x0080, ///< On logging in: The username - LOGIN_PASSWORD ///< On logging in: The password - }; - Q_ENUM(Type) - /** - * @return the type of the prompt - */ - Type type() const; - /** - * @warning the preferred way is to use \ref type - * @return message from the stack - */ - QString message() const; - /** - * @return true if user's input should not be shown in readable form - */ - bool hidden() const; - /** - * Public getter for the response data. - * The property is write-only though, so it returns garbage. - * Contained only to keep the MOC parser happy. - * @warning do not use, doesn't return valid data - * @return empty byte array - */ - QByteArray responseFake(); - /** - * Setter for the response data - * @param r data entered by the user - */ - void setResponse(const QByteArray &r); - Q_SIGNALS: - /** - * Emitted when the response was entered by the user - */ - void responseChanged(); - private: - AuthPrompt(const Prompt *prompt, AuthRequest *parent = 0); - QByteArray response() const; - friend class AuthRequest; - class Private; - Private *d { nullptr }; - }; -} - -#endif //PROMPT_H diff --git a/src/auth/AuthRequest.cpp b/src/auth/AuthRequest.cpp deleted file mode 100644 index 34ac109..0000000 --- a/src/auth/AuthRequest.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "AuthRequest.h" -#include "Auth.h" -#include "AuthMessages.h" - -namespace DDM { - class AuthRequest::Private : public QObject { - Q_OBJECT - public slots: - void responseChanged(); - public: - Private(QObject *parent); - QList prompts { }; - bool finishAutomatically { false }; - bool finished { true }; - }; - - AuthRequest::Private::Private(QObject* parent) - : QObject(parent) { } - - void AuthRequest::Private::responseChanged() { - for (const AuthPrompt *qap : std::as_const(prompts)) { - if (qap->response().isEmpty()) - return; - } - if (finishAutomatically && prompts.length() > 0) - qobject_cast(parent())->done(); - } - - AuthRequest::AuthRequest(Auth *parent) - : QObject(parent) - , d(new Private(this)) { } - - void AuthRequest::setRequest(const Request *request) { - QList promptsCopy(d->prompts); - d->prompts.clear(); - if (request != nullptr) { - for (const Prompt &p : std::as_const(request->prompts)) { - AuthPrompt *qap = new AuthPrompt(&p, this); - d->prompts << qap; - if (finishAutomatically()) - connect(qap, &AuthPrompt::responseChanged, d, &AuthRequest::Private::responseChanged); - } - d->finished = false; - } - Q_EMIT promptsChanged(); - if (request == nullptr) { - qDeleteAll(promptsCopy); - } - } - - QList AuthRequest::prompts() { - return d->prompts; - } - - QQmlListProperty AuthRequest::promptsDecl() { - return QQmlListProperty(this, &d->prompts); - } - - void AuthRequest::done() { - if (!d->finished) { - d->finished = true; - Q_EMIT finished(); - } - } - - bool AuthRequest::finishAutomatically() { - return d->finishAutomatically; - } - - void AuthRequest::setFinishAutomatically(bool value) { - if (value != d->finishAutomatically) { - d->finishAutomatically = value; - Q_EMIT finishAutomaticallyChanged(); - } - } - - Request AuthRequest::request() const { - Request r; - for (const AuthPrompt *qap : std::as_const(d->prompts)) { - Prompt p; - p.hidden = qap->hidden(); - p.message = qap->message(); - p.response = qap->response(); - p.type = qap->type(); - r.prompts << p; - } - return r; - } -} - -#include "AuthRequest.moc" diff --git a/src/auth/AuthRequest.h b/src/auth/AuthRequest.h deleted file mode 100644 index bb6f6a3..0000000 --- a/src/auth/AuthRequest.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Qt Authentication library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef REQUEST_H -#define REQUEST_H - -#include - -#include - -namespace DDM { - class Auth; - class AuthPrompt; - class Request; - /** - * \brief - * AuthRequest is the main class for tracking requests from the underlying auth stack - * - * \section description - * Typically, when logging in, you'll receive a list containing one or two fields: - * - * * First one for the username (if you didn't provide it before); - * hidden = false, type = LOGIN_USER, message = whatever the stack provides - * - * * Second one for the user's password - * hidden = true, type = LOGIN_PASSWORD, message = whatever the stack provides - * - * It's up to you to fill the \ref AuthPrompt::response property. - * When all the fields are filled to your satisfaction, just trigger the \ref done - * slot and the response will go back to the authenticator. - * - * \todo Decide if it's sane to use the info messages from PAM or to somehow parse them - * and make the password changing message into a Request::Type of some kind - */ - class AuthRequest : public QObject { - Q_OBJECT - Q_PROPERTY(QQmlListProperty prompts READ promptsDecl NOTIFY promptsChanged) - Q_PROPERTY(bool finishAutomatically READ finishAutomatically WRITE setFinishAutomatically NOTIFY finishAutomaticallyChanged) - public: - /** - * @return list of the contained prompts - */ - QList prompts(); - /** - * For QML apps - * @return list of the contained prompts - */ - QQmlListProperty promptsDecl(); - - static AuthRequest *empty(); - - bool finishAutomatically(); - void setFinishAutomatically(bool value); - public Q_SLOTS: - /** - * Call this slot when all prompts has been filled to your satisfaction - */ - void done(); - Q_SIGNALS: - /** - * Emitted when \ref done was called - */ - void finished(); - - void finishAutomaticallyChanged(); - void promptsChanged(); - private: - AuthRequest(Auth *parent); - void setRequest(const Request *request = nullptr); - Request request() const; - friend class Auth; - class Private; - Private *d { nullptr }; - }; -} - -#endif //REQUEST_H diff --git a/src/auth/CMakeLists.txt b/src/auth/CMakeLists.txt deleted file mode 100644 index a44c598..0000000 --- a/src/auth/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -set(PUBLIC_HEADERS - ${CMAKE_SOURCE_DIR}/src/auth/Auth.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthMessages.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.h -) - -set(SRCS - ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp -) - -add_library(auth SHARED ${PUBLIC_HEADERS} ${SRCS}) - -set_target_properties(auth PROPERTIES - VERSION ${CMAKE_PROJECT_VERSION} - SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} - OUTPUT_NAME ddm-auth - EXPORT_NAME Auth -) - -target_link_libraries(auth - PUBLIC - common - Qt${QT_MAJOR_VERSION}::Network - Qt${QT_MAJOR_VERSION}::Qml -) - -target_include_directories(auth INTERFACE - $ - $ - $ -) -target_link_directories(auth INTERFACE - $ - $ -) - -if(PAM_FOUND) - target_link_libraries(auth PRIVATE ${PAM_LIBRARIES}) -else() - target_link_libraries(auth PRIVATE crypt) -endif() - -# 安装库和头文件 -install( - TARGETS auth - EXPORT AuthTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION bin - INCLUDES DESTINATION include/ddm/auth -) - -install(FILES ${PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ddm/auth") - -# 导出目标文件 -install(EXPORT AuthTargets - FILE AuthTargets.cmake - NAMESPACE DDM:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/DDM -) diff --git a/src/daemon/Auth.cpp b/src/daemon/Auth.cpp new file mode 100644 index 0000000..002211b --- /dev/null +++ b/src/daemon/Auth.cpp @@ -0,0 +1,202 @@ +/* + * Qt Authentication Library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "Auth.h" + +#include "Pam.h" +#include "UserSession.h" + +#include +#include +#include +#include + +namespace DDM { + int Auth::lastId = 0; + + Auth::Auth(QObject *parent) + : QObject(parent) + , id(++lastId) + , m_pam(new Pam(this)) + , m_session(new UserSession(this)) { + connect(this, &Auth::finished, this, [this] { + active = false; + }); + } + + Auth::~Auth() { + stop(); + } + +#define LOGIN_FAILED \ + const QString displayId = environment.value(QStringLiteral("DISPLAY")); \ + const QString vt = environment.value(QStringLiteral("XDG_VTNR")); \ + utmpLogin(vt, displayId, user, 0, false); \ + Q_EMIT finished(AUTH_ERROR); \ + return; + + void Auth::start(const QByteArray &secret) { + Q_ASSERT(getuid() == 0); + + active = true; + m_pam->user = user; + if (!m_pam->start()) { + Q_EMIT authentication(user, false, identifyOnly); + LOGIN_FAILED + } + + if (!skipAuth && !m_pam->authenticate(secret)) { + Q_EMIT authentication(user, false, identifyOnly); + LOGIN_FAILED + } + + Q_EMIT authentication(user, true, identifyOnly); + + if (!sessionPath.isEmpty()) { + auto sessionEnv = m_pam->openSession(environment); + if (!sessionEnv.has_value()) { + Q_EMIT sessionStarted(false, 0); + Q_EMIT finished(SESSION_ERROR); + return; + } + + QProcessEnvironment env = *sessionEnv; + xdgSessionId = env.value(QStringLiteral("XDG_SESSION_ID")).toInt(); + Q_EMIT sessionStarted(true, xdgSessionId); + + struct passwd *pw; + pw = getpwnam(qPrintable(user)); + if (pw) { + env.insert(QStringLiteral("HOME"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("PWD"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); + env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); + env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); + } + m_session->setProcessEnvironment(env); + m_session->start(); + + // write successful login to utmp/wtmp + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); + // cache pid for session end + utmpLogin(vt, displayId, user, m_session->processId(), true); + } + return; + } + + void Auth::stop() { + Q_ASSERT(getuid() == 0); + + qint64 pid = m_session->processId(); + m_session->stop(); + if (!identifyOnly && m_pam->sessionOpened) { + m_pam->closeSession(); + } + + // write logout to utmp/wtmp + if (pid <= 0) + return; + QProcessEnvironment env = m_session->processEnvironment(); + QString vt = env.value(QStringLiteral("XDG_VTNR")); + QString displayId = env.value(QStringLiteral("DISPLAY")); + utmpLogout(vt, displayId, pid); + } + + void Auth::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = USER_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + // ut_user: user + QByteArray userBa = user.toLocal8Bit(); + const char* userChar = userBa.constData(); + strncpy(entry.ut_user, userChar, sizeof(entry.ut_user) -1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + + // append to failed login database btmp + if (!authSuccessful) { + updwtmpx("/var/log/btmp", &entry); + } else { + // append to wtmp + updwtmpx("/var/log/wtmp", &entry); + } + } + + void Auth::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = DEAD_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + + // append to wtmp + updwtmpx("/var/log/wtmp", &entry); + } +} // namespace DDM diff --git a/src/daemon/Auth.h b/src/daemon/Auth.h new file mode 100644 index 0000000..39a942f --- /dev/null +++ b/src/daemon/Auth.h @@ -0,0 +1,128 @@ +/* + * Qt Authentication library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DDM_AUTH_H +#define DDM_AUTH_H + +#include "Session.h" + +#include +#include + +namespace DDM { + class Pam; + class UserSession; + + class Auth : public QObject { + Q_OBJECT + public: + Auth(QObject *parent); + ~Auth(); + + enum ExitStatus { + SUCCESS = 0, + AUTH_ERROR, + SESSION_ERROR, + OTHER_ERROR, + DISPLAYSERVER_ERROR, + TTY_ERROR, + }; + Q_ENUM(ExitStatus) + + bool active{ false }; + QString displayServerCmd{}; + QString sessionPath{}; + Session::Type sessionType{ Session::UnknownSession }; + QString sessionFileName{}; + QString user{}; + QByteArray cookie{}; + bool autologin{ false }; + bool greeter{ false }; + bool singleMode{ false }; + bool identifyOnly{ false }; + bool skipAuth{ false }; + QProcessEnvironment environment{ }; + int id{ 0 }; + static int lastId; + QString sessionId{}; + int tty{ 0 }; + int xdgSessionId{ 0 }; + public Q_SLOTS: + /** + * Sets up the environment and starts the authentication + */ + void start(const QByteArray &secret); + + /** + * Indicates that we do not need the process anymore. + */ + void stop(); + + Q_SIGNALS: + /** + * Emitted when authentication phase finishes + * + * @note If you want to set some environment variables for the session right before the + * session is started, connect to this signal using a blocking connection and insert anything + * you need in the slot. + * @param user username + * @param success true if succeeded + */ + void authentication(QString user, bool success, bool identifyOnly); + + /** + * Emitted when session starting phase finishes + * + * @param success true if succeeded + */ + void sessionStarted(bool success, int xdgSessionId); + + /** + * Emitted when the session ends. + * + * @param success true if every underlying task went fine + */ + void finished(Auth::ExitStatus status); + + private: + Pam *m_pam { nullptr }; + UserSession *m_session{ nullptr }; + + /** + * Write utmp/wtmp/btmp records when a user logs in + * @param vt Virtual terminal (tty7, tty8,...) + * @param displayName Display (:0, :1,...) + * @param user User logging in + * @param pid User process ID (e.g. PID of startkde) + * @param authSuccessful Was authentication successful + */ + void utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful); + + /** + * Write utmp/wtmp records when a user logs out + * @param vt Virtual terminal (tty7, tty8,...) + * @param displayName Display (:0, :1,...) + * @param pid User process ID (e.g. PID of startkde) + */ + void utmpLogout(const QString &vt, const QString &displayName, qint64 pid); + }; +} + +#endif // DDM_AUTH_H diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index b5721b5..013f325 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -25,16 +25,19 @@ add_custom_command( configure_file(config.h.in config.h IMMEDIATE @ONLY) set(DAEMON_SOURCES + Auth.cpp DaemonApp.cpp Display.cpp DisplayManager.cpp DisplayServer.cpp Greeter.cpp + Pam.cpp PowerManager.cpp Seat.cpp SeatManager.cpp SocketServer.cpp TreelandConnector.cpp + UserSession.cpp XorgDisplayServer.cpp XorgUserDisplayServer.cpp XorgUserDisplayServer.h @@ -73,12 +76,13 @@ qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.fr add_executable(ddm ${DAEMON_SOURCES}) target_link_libraries(ddm PRIVATE - auth + common PUBLIC Qt${QT_MAJOR_VERSION}::DBus Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Qml ${LIBXCB_LIBRARIES} + ${PAM_LIBRARIES} PkgConfig::WAYLAND ) diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index e3101ef..e6c2ff7 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -181,7 +181,7 @@ namespace DDM { connect(m_greeter, &Greeter::displayServerFailed, this, &Display::displayServerFailed); connect(m_greeter, &Greeter::greeterStarted, this, [this] { if (m_currentAuth) { - switchToUser(m_currentAuth->user(), m_currentAuth->xdgSessionId()); + switchToUser(m_currentAuth->user, m_currentAuth->xdgSessionId); } }); } @@ -205,7 +205,7 @@ namespace DDM { int Display::terminalId() const { // TODO: if not use single wayland mode, m_auths only have one element. - return !m_auths.isEmpty() && m_auths.first()->isActive() ? m_sessionTerminalId : m_terminalId; + return !m_auths.isEmpty() && m_auths.first()->active ? m_sessionTerminalId : m_terminalId; } const QString &Display::name() const { @@ -358,8 +358,8 @@ namespace DDM { // send logined user (for possible crash recovery) SocketWriter writer(socket); for (Auth *auth : loginedSession()) { - if (auth->isActive()) - writer << quint32(DaemonMessages::UserLoggedIn) << auth->user() << auth->xdgSessionId(); + if (auth->active) + writer << quint32(DaemonMessages::UserLoggedIn) << auth->user << auth->xdgSessionId; } } @@ -446,18 +446,12 @@ namespace DDM { auth->setObjectName("userIdentify"); m_auths.push_back(auth); - auth->setVerbose(true); - connect(auth, &Auth::requestChanged, this, &Display::slotRequestChanged); connect(auth, &Auth::authentication, this, &Display::slotAuthenticationFinished); connect(auth, &Auth::finished, this, &Display::slotHelperFinished); - connect(auth, &Auth::info, this, &Display::slotAuthInfo); - connect(auth, &Auth::error, this, &Display::slotAuthError); - - auth->setVerbose(true); - auth->setPassword(password); - auth->setUser(user); - auth->setIdentifyOnly(true); - auth->start(); + + auth->user = user; + auth->identifyOnly = true; + auth->start(password.toLocal8Bit()); } void Display::startAuth(const QString &user, const QString &password, const Session &session) { @@ -465,7 +459,7 @@ namespace DDM { // respond to authentication requests Auth *auth = nullptr; for (auto *item : m_auths) { - if (item->user() == user) { + if (item->user == user) { auth = item; break; } @@ -476,22 +470,15 @@ namespace DDM { m_auths << auth; } - auth->setVerbose(true); - auth->setAutologin(mainConfig.Autologin.User.get() == user); - connect(auth, &Auth::requestChanged, this, &Display::slotRequestChanged); connect(auth, &Auth::authentication, this, &Display::slotAuthenticationFinished); connect(auth, &Auth::sessionStarted, this, &Display::slotSessionStarted); connect(auth, &Auth::finished, this, &Display::slotHelperFinished); - connect(auth, &Auth::info, this, &Display::slotAuthInfo); - connect(auth, &Auth::error, this, &Display::slotAuthError); - if (auth->isActive()) { + if (auth->active) { qWarning() << "Existing authentication ongoing, aborting"; return; } - auth->setPassword(password); - // sanity check if (!session.isValid()) { qCritical() << "Invalid session" << session.fileName(); @@ -525,14 +512,14 @@ namespace DDM { } } - auth->setTTY(m_terminalId); + auth->tty = m_terminalId; if ((session.type() == Session::WaylandSession && m_displayServerType == X11DisplayServerType) || (m_greeter->isRunning() && m_displayServerType != X11DisplayServerType) || (m_displayServerType == SingleCompositerServerType)) { // Create a new VT when we need to have another compositor running - auth->setTTY(VirtualTerminal::setUpNewVt()); + auth->tty = VirtualTerminal::setUpNewVt(); } // some information - qDebug() << "Session" << session.fileName() << "selected, command:" << session.exec() << "for VT" << auth->tty(); + qDebug() << "Session" << session.fileName() << "selected, command:" << session.exec() << "for VT" << auth->tty; QProcessEnvironment env; env.insert(session.additionalEnv()); @@ -542,9 +529,9 @@ namespace DDM { // session id { const QString sessionId = QStringLiteral("Session%1").arg(daemonApp->newSessionId()); - daemonApp->displayManager()->AddSession(sessionId, seat()->name(), user, auth->tty()); + daemonApp->displayManager()->AddSession(sessionId, seat()->name(), user, auth->tty); env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(sessionId)); - auth->setSessionId(sessionId); + auth->sessionId = sessionId; } env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); @@ -552,7 +539,7 @@ namespace DDM { env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user")); env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType()); - env.insert(QStringLiteral("XDG_VTNR"), QString::number(auth->tty())); + env.insert(QStringLiteral("XDG_VTNR"), QString::number(auth->tty)); env.insert(QStringLiteral("XDG_SEAT"), seat()->name()); env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name())); @@ -564,40 +551,40 @@ namespace DDM { if (m_displayServerType == X11DisplayServerType) env.insert(QStringLiteral("DISPLAY"), name()); else - auth->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + auth->displayServerCmd = XorgUserDisplayServer::command(this); } else { if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { env.insert("DDE_CURRENT_COMPOSITOR", "TreeLand"); } - auth->setDisplayServerCommand(QStringLiteral()); + auth->displayServerCmd = QStringLiteral(); } - auth->setUser(user); - auth->setSessionType(session.type()); - auth->setSessionFileName(session.fileName()); + auth->user = user; + auth->sessionType = session.type(); + auth->sessionFileName = session.fileName(); if (m_reuseSessionId.isNull()) { - auth->setSession(session.exec()); + auth->sessionPath = session.exec(); } - auth->insertEnvironment(env); - auth->setSingleMode(session.isSingleMode()); - auth->start(); + auth->environment.insert(env); + auth->singleMode = session.isSingleMode(); + auth->start(password.toLocal8Bit()); } void Display::slotAuthenticationFinished(const QString &user, bool success, bool identifyOnly) { Auth* auth = nullptr; if(identifyOnly){ - auto ret = std::find_if(m_auths.crbegin(),m_auths.crend(),[&user](Auth *ptr) { qDebug() << ptr->user(); return ptr->user() == user;}); + auto ret = std::find_if(m_auths.crbegin(),m_auths.crend(),[&user](Auth *ptr) { qDebug() << ptr->user; return ptr->user == user;}); Q_ASSERT(ret != m_auths.crend()); auth = *ret; } else { - auto ret = std::find_if(m_auths.cbegin(),m_auths.cend(),[&user](Auth *ptr) { return ptr->user() == user;}); + auto ret = std::find_if(m_auths.cbegin(),m_auths.cend(),[&user](Auth *ptr) { return ptr->user == user;}); Q_ASSERT(ret != m_auths.cend()); auth = *ret; } - Q_ASSERT(auth && auth->identifyOnly() == identifyOnly); + Q_ASSERT(auth && auth->identifyOnly == identifyOnly); m_currentAuth = auth; m_greeter->setUserActivated(success); @@ -611,38 +598,38 @@ namespace DDM { m_started = true; } else { if (qobject_cast(m_displayServer)) - auth->setCookie(qobject_cast(m_displayServer)->cookie()); + auth->cookie = qobject_cast(m_displayServer)->cookie(); } // save last user and last session if (mainConfig.Users.RememberLastUser.get()) - stateConfig.Last.User.set(auth->user()); + stateConfig.Last.User.set(auth->user); else stateConfig.Last.User.setDefault(); if (mainConfig.Users.RememberLastSession.get()) - stateConfig.Last.Session.set(auth->sessionFileName()); + stateConfig.Last.Session.set(auth->sessionFileName); else stateConfig.Last.Session.setDefault(); stateConfig.save(); - if (auth->identifyOnly()) { + if (auth->identifyOnly) { auto* server = reinterpret_cast(m_displayServer); server->onLoginSucceeded(user); // TODO: Use exact ID when there're multiple sessions for a user int xdgSessionId = 0; for (auto *auth : m_auths) - if (auth->user() == user && auth->xdgSessionId() > 0) { - xdgSessionId = auth->xdgSessionId(); + if (auth->user == user && auth->xdgSessionId > 0) { + xdgSessionId = auth->xdgSessionId; break; } switchToUser(user, xdgSessionId); - } else if (auth->isSingleMode()) { + } else if (auth->singleMode) { auto* server = reinterpret_cast(m_displayServer); server->onLoginSucceeded(user); } else { if (m_socket) { emit loginSucceeded(m_socket, user); - daemonApp->displayManager()->setLastSession(auth->sessionId()); + daemonApp->displayManager()->setLastSession(auth->sessionId); } // Stop the original suit of displayServer since it has finished its job. @@ -659,7 +646,7 @@ namespace DDM { m_terminalId = m_sessionTerminalId = fetchAvailableVt(); m_started = false; - if (auth->sessionType() == Session::X11Session) { + if (auth->sessionType == Session::X11Session) { m_displayServer = new XorgDisplayServer(this); m_displayServerType = X11DisplayServerType; } else { @@ -673,7 +660,7 @@ namespace DDM { // The greeter here is used to start user session. m_greeter = new Greeter(this); - m_greeter->setDisplayServerCommand(auth->session()); + m_greeter->setDisplayServerCommand(auth->sessionPath); m_greeter->setUser(user); m_greeter->setSkipAuth(); @@ -687,35 +674,7 @@ namespace DDM { m_socket = nullptr; } - void Display::slotAuthInfo(const QString &message, Auth::Info info) { - qWarning() << "Authentication information:" << info << message; - - if (!m_socket) - return; - - m_socketServer->informationMessage(m_socket, message); - } - - void Display::slotAuthError(const QString &message, Auth::Error error) { - Auth* auth = qobject_cast(sender()); - - qWarning() << "Authentication error:" << error << message; - - if (!m_socket) - return; - - m_socketServer->informationMessage(m_socket, message); - if (error == Auth::ERROR_AUTHENTICATION) { - if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { - auto* server = reinterpret_cast(m_displayServer); - server->onLoginFailed(auth->user()); - } else { - emit loginFailed(m_socket, auth->user()); - } - } - } - - void Display::slotHelperFinished(Auth::HelperExitStatus status) { + void Display::slotHelperFinished(Auth::ExitStatus status) { Auth* auth = qobject_cast(sender()); // Don't restart greeter and display server unless ddm-helper exited // with an internal error or the user session finished successfully, @@ -731,8 +690,8 @@ namespace DDM { m_auths.removeOne(auth); auth->deleteLater(); - if(!auth->identifyOnly()) { - daemonApp->displayManager()->RemoveSession(auth->sessionId()); + if(!auth->identifyOnly) { + daemonApp->displayManager()->RemoveSession(auth->sessionId); if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { auto* server = reinterpret_cast(m_displayServer); // TODO: switch to greeter @@ -745,28 +704,15 @@ namespace DDM { } // Don't restart display when ddm-helper crashed (exit code 9) - if (status != Auth::HELPER_AUTH_ERROR && status != Auth::HelperExitStatus(9) && m_displayServerType != DisplayServerType::SingleCompositerServerType) + if (status != Auth::AUTH_ERROR && status != Auth::ExitStatus(9) && m_displayServerType != DisplayServerType::SingleCompositerServerType) stop(); } - void Display::slotRequestChanged() { - Auth* auth = qobject_cast(sender()); - if (auth->request()->prompts().length() == 1) { - auth->request()->prompts()[0]->setResponse(qPrintable(auth->password())); - auth->request()->done(); - } else if (auth->request()->prompts().length() == 2) { - auth->request()->prompts()[0]->setResponse(qPrintable(auth->user())); - auth->request()->prompts()[1]->setResponse(qPrintable(auth->password())); - auth->request()->done(); - } - } - void Display::slotSessionStarted(bool success, int xdgSessionId) { qDebug() << "Session started" << success; Auth* auth = qobject_cast(sender()); - auth->setXdgSessionId(xdgSessionId); - if (auth->isSingleMode()) { - switchToUser(auth->user(), xdgSessionId); + if (auth->singleMode) { + switchToUser(auth->user, xdgSessionId); } if (success && m_displayServerType != SingleCompositerServerType) { diff --git a/src/daemon/Display.h b/src/daemon/Display.h index b0f4939..593dbca 100644 --- a/src/daemon/Display.h +++ b/src/daemon/Display.h @@ -120,12 +120,9 @@ namespace DDM { Greeter *m_greeter { nullptr }; private slots: - void slotRequestChanged(); void slotAuthenticationFinished(const QString &user, bool success, bool identifyOnly); void slotSessionStarted(bool success, int xdgSessionId); - void slotHelperFinished(Auth::HelperExitStatus status); - void slotAuthInfo(const QString &message, Auth::Info info); - void slotAuthError(const QString &message, Auth::Error error); + void slotHelperFinished(Auth::ExitStatus status); }; } diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp index 28797c8..3d948e5 100644 --- a/src/daemon/Greeter.cpp +++ b/src/daemon/Greeter.cpp @@ -190,13 +190,8 @@ namespace DDM { } else { // authentication m_auth = new Auth(this); - m_auth->setVerbose(true); - connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); - connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); - connect(m_auth, &Auth::info, this, &Greeter::authInfo); - connect(m_auth, &Auth::error, this, &Greeter::authError); // command QStringList cmd; @@ -235,7 +230,7 @@ namespace DDM { env.insert(QStringLiteral("DISPLAY"), m_display->name()); env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); if (m_display->sessionType() == "x11") - m_auth->setCookie(qobject_cast(displayServer)->cookie()); + m_auth->cookie = qobject_cast(displayServer)->cookie(); } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); @@ -243,21 +238,21 @@ namespace DDM { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); } - m_auth->insertEnvironment(env); + m_auth->environment.insert(env); // log message qDebug() << "Greeter starting..."; // start greeter - m_auth->setUser(m_user); + m_auth->user = m_user; QString displayServerCmd = m_displayServerCmd; if (m_singleMode) { displayServerCmd += " --lockscreen"; } - m_auth->setDisplayServerCommand(displayServerCmd); - m_auth->setGreeter(true); - m_auth->setSession(cmd.join(QLatin1Char(' '))); - m_auth->setSingleMode(m_singleMode); + m_auth->displayServerCmd = displayServerCmd; + m_auth->greeter = true; + m_auth->sessionPath = cmd.join(QLatin1Char(' ')); + m_auth->singleMode = m_singleMode; // TODO: single compositer mode not need greeter if (m_singleMode) { @@ -265,10 +260,10 @@ namespace DDM { } if (m_skipAuth) { - m_auth->setSkipAuth(); + m_auth->skipAuth = true; } - m_auth->start(); + m_auth->start(""); m_tryTimer->start(); } @@ -321,10 +316,6 @@ namespace DDM { } } - void Greeter::onRequestChanged() { - m_auth->request()->setFinishAutomatically(true); - } - void Greeter::onSessionStarted(bool success,[[maybe_unused]] int xdgSessionId) { // set flag m_started = success; @@ -336,20 +327,7 @@ namespace DDM { qDebug() << "Greeter session failed to start"; } - void Greeter::onDisplayServerReady(const QString &displayName) - { - auto *displayServer = m_display->displayServer(); - - auto *xorgUser = qobject_cast(displayServer); - if (xorgUser) - xorgUser->setDisplayName(displayName); - - auto *wayland = qobject_cast(displayServer); - if (wayland) - wayland->setDisplayName(displayName); - } - - void Greeter::onHelperFinished(Auth::HelperExitStatus status) { + void Greeter::onHelperFinished(Auth::ExitStatus status) { if (m_singleMode && m_currentRetry <= m_maxRetry) { m_currentRetry += 1; qDebug() << "Restart treeland"; @@ -357,8 +335,8 @@ namespace DDM { if (!m_userActivated) { displayServerCmd += " --lockscreen"; } - m_auth->setDisplayServerCommand(displayServerCmd); - m_auth->start(); + m_auth->displayServerCmd = displayServerCmd; + m_auth->start(""); if (m_tryTimer->isActive()) { m_tryTimer->stop(); @@ -378,20 +356,20 @@ namespace DDM { m_auth->deleteLater(); m_auth = nullptr; - if (status == Auth::HELPER_DISPLAYSERVER_ERROR) { + if (status == Auth::DISPLAYSERVER_ERROR) { Q_EMIT displayServerFailed(); - } else if (status == Auth::HELPER_TTY_ERROR) { + } else if (status == Auth::TTY_ERROR) { Q_EMIT ttyFailed(); - } else if (status == Auth::HELPER_SESSION_ERROR) { + } else if (status == Auth::SESSION_ERROR) { Q_EMIT failed(); - } else if (status == Auth::HELPER_SUCCESS) { + } else if (status == Auth::SUCCESS) { Q_EMIT succeed(); } } bool Greeter::isRunning() const { return (m_process && m_process->state() == QProcess::Running) - || (m_auth && m_auth->isActive()); + || (m_auth && m_auth->active); } void Greeter::onReadyReadStandardError() @@ -407,14 +385,4 @@ namespace DDM { qDebug() << "Greeter output:" << m_process->readAllStandardOutput().constData(); } } - - void Greeter::authInfo(const QString &message, Auth::Info info) { - Q_UNUSED(info); - qDebug() << "Information from greeter session:" << message; - } - - void Greeter::authError(const QString &message, Auth::Error error) { - Q_UNUSED(error); - qWarning() << "Error from greeter session:" << message; - } } diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h index 8f08b3e..c3ee9f6 100644 --- a/src/daemon/Greeter.h +++ b/src/daemon/Greeter.h @@ -56,14 +56,10 @@ namespace DDM { void finished(); private slots: - void onRequestChanged(); void onSessionStarted(bool success, int xdgSessionId); - void onDisplayServerReady(const QString &displayName); - void onHelperFinished(Auth::HelperExitStatus status); + void onHelperFinished(Auth::ExitStatus status); void onReadyReadStandardOutput(); void onReadyReadStandardError(); - void authInfo(const QString &message, Auth::Info info); - void authError(const QString &message, Auth::Error error); signals: void ttyFailed(); diff --git a/src/helper/Pam.cpp b/src/daemon/Pam.cpp similarity index 100% rename from src/helper/Pam.cpp rename to src/daemon/Pam.cpp diff --git a/src/helper/Pam.h b/src/daemon/Pam.h similarity index 100% rename from src/helper/Pam.h rename to src/daemon/Pam.h diff --git a/src/helper/UserSession.cpp b/src/daemon/UserSession.cpp similarity index 71% rename from src/helper/UserSession.cpp rename to src/daemon/UserSession.cpp index 757d8ab..018150e 100644 --- a/src/helper/UserSession.cpp +++ b/src/daemon/UserSession.cpp @@ -19,18 +19,18 @@ * */ +#include #include #include "Configuration.h" #include "Constants.h" #include "UserSession.h" -#include "HelperApp.h" +#include "Auth.h" #include "VirtualTerminal.h" #include "XAuth.h" #include #include -#include #include #include #include @@ -42,54 +42,43 @@ #include #include #include -#ifdef Q_OS_FREEBSD -#include -#endif namespace DDM { - UserSession::UserSession(HelperApp *parent) + UserSession::UserSession(Auth *parent) : QProcess(parent) - , m_helperApp(parent) + , m_auth(parent) { - connect(this, QOverload::of(&QProcess::finished), this, &UserSession::finished); + connect(this, + QOverload::of(&QProcess::finished), + this, + [this](int code) { + Q_EMIT m_auth->finished(static_cast(code)); + }); setChildProcessModifier(std::bind(&UserSession::childModifier, this)); } - bool UserSession::start() { - auto helper = qobject_cast(parent()); + void UserSession::start() { QProcessEnvironment env = processEnvironment(); - bool isWaylandGreeter = false; - - if (m_helperApp->isSingleMode()) { - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/ddm-helper-start-single-wayland")); - // TODO: should we use 'treeland' command? - setArguments({m_displayServerCmd, m_path}); - QProcess::start(); - isWaylandGreeter = true; - } - else { - setProgram(mainConfig.Single.SessionCommand.get()); - setArguments(QStringList{m_path}); - qInfo() << "Starting Wayland user session:" << program() << m_path; - QProcess::start(); - closeWriteChannel(); - closeReadChannel(QProcess::StandardOutput); - } + if (m_auth->singleMode) { + setProgram(mainConfig.Single.SessionCommand.get()); + setArguments(QStringList{ m_auth->sessionPath }); + qInfo() << "Starting Wayland user session:" << program() << m_auth->sessionPath; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); } else { // If the Xorg display server was already started, write the passed // auth cookie to /tmp/xauth_XXXXXX. This is done in the parent process // so that it can clean up the file on session end. if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { - // Create the Xauthority file - QByteArray cookie = helper->cookie(); + QByteArray cookie = m_auth->cookie; if (cookie.isEmpty()) { qCritical() << "Can't start X11 session with empty auth cookie"; - return false; + return; } - + // Create the Xauthority file // Place it into /tmp, which is guaranteed to be read/writeable by // everyone while having the sticky bit set to avoid messing with // other's files. @@ -97,7 +86,7 @@ namespace DDM { if (!m_xauthFile.open()) { qCritical() << "Could not create the Xauthority file"; - return false; + return; } QString display = processEnvironment().value(QStringLiteral("DISPLAY")); @@ -105,55 +94,29 @@ namespace DDM { if (!XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)) { qCritical() << "Failed to write the Xauthority file"; m_xauthFile.close(); - return false; + return; } env.insert(QStringLiteral("XAUTHORITY"), m_xauthFile.fileName()); setProcessEnvironment(env); - QString command; - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - command = m_path; - } else { - command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); - } - - qInfo() << "Starting X11 session:" << m_displayServerCmd << command; - auto args = QProcess::splitCommand(m_displayServerCmd); + QString command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_auth->sessionPath); + qInfo() << "Starting X11 session:" << m_auth->displayServerCmd << command; + auto args = QProcess::splitCommand(m_auth->displayServerCmd); setProgram(args.takeFirst()); setArguments(args); QProcess::start(); - } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - Q_ASSERT(!m_displayServerCmd.isEmpty()); - setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/ddm-helper-start-wayland")); - setArguments({m_displayServerCmd, m_path}); - QProcess::start(); - isWaylandGreeter = true; - } else { - setProgram(mainConfig.Wayland.SessionCommand.get()); - setArguments(QStringList{m_path}); - qInfo() << "Starting Wayland user session:" << program() << m_path; - QProcess::start(); - closeWriteChannel(); - closeReadChannel(QProcess::StandardOutput); - } + setProgram(mainConfig.Wayland.SessionCommand.get()); + setArguments(QStringList{ m_auth->sessionPath }); + qInfo() << "Starting Wayland user session:" << program() << m_auth->sessionPath; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); } else { qCritical() << "Unable to run user session: unknown session type"; } } - - const bool started = waitForStarted(); - m_cachedProcessId = processId(); - if (started) { - return true; - } else if (isWaylandGreeter) { - // This is probably fine, we need the compositor to start first - return true; - } - - return false; } void UserSession::stop() @@ -170,33 +133,15 @@ namespace DDM { } } } else { - Q_EMIT finished(Auth::HELPER_OTHER_ERROR); + Q_EMIT finished(Auth::OTHER_ERROR); } } - QString UserSession::displayServerCommand() const - { - return m_displayServerCmd; - } - - void UserSession::setDisplayServerCommand(const QString &command) - { - m_displayServerCmd = command; - } - - void UserSession::setPath(const QString& path) { - m_path = path; - } - - QString UserSession::path() const { - return m_path; - } - void UserSession::childModifier() { // Session type QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE")); QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")); - const bool hasDisplayServer = !m_displayServerCmd.isEmpty(); + const bool hasDisplayServer = !m_auth->displayServerCmd.isEmpty(); const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user"); // When the display server is part of the session, we leak the VT into @@ -224,7 +169,7 @@ namespace DDM { if (setsid() < 0) { qCritical("Failed to set pid %lld as leader of the new session and process group: %s", QCoreApplication::applicationPid(), strerror(errno)); - _exit(Auth::HELPER_OTHER_ERROR); + _exit(Auth::OTHER_ERROR); } @@ -233,36 +178,34 @@ namespace DDM { if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) { const auto error = strerror(errno); qCritical().nospace() << "Failed to take control of " << ttyString << " (" << QFileInfo(ttyString).owner() << "): " << error; - _exit(Auth::HELPER_TTY_ERROR); + _exit(Auth::TTY_ERROR); } if (ioctl(STDIN_FILENO, KDSKBMODE, K_OFF) == -1) { qCritical().nospace() << "Failed to set keyboard mode to K_OFF"; - _exit(Auth::HELPER_TTY_ERROR); + _exit(Auth::TTY_ERROR); } } VirtualTerminal::jumpToVt(vtNumber, !waylandUserSession); } -#ifdef Q_OS_LINUX // enter Linux namespaces for (const QString &ns: mainConfig.Namespaces.get()) { qInfo() << "Entering namespace" << ns; int fd = ::open(qPrintable(ns), O_RDONLY); if (fd < 0) { qCritical("open(%s) failed: %s", qPrintable(ns), strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } if (setns(fd, 0) != 0) { qCritical("setns(open(%s), 0) failed: %s", qPrintable(ns), strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } ::close(fd); } -#endif // switch user - const QByteArray username = qobject_cast(parent())->user().toLocal8Bit(); + const QByteArray username = qobject_cast(parent())->user.toLocal8Bit(); struct passwd pw; struct passwd *rpw; long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); @@ -271,7 +214,7 @@ namespace DDM { QScopedPointer buffer(static_cast(malloc(bufsize))); if (buffer.isNull()) { qCritical() << "Could not allocate buffer of size" << bufsize; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } int err = getpwnam_r(username.constData(), &pw, buffer.data(), bufsize, &rpw); if (rpw == NULL) { @@ -279,26 +222,18 @@ namespace DDM { qCritical() << "getpwnam_r(" << username << ") username not found!"; else qCritical() << "getpwnam_r(" << username << ") failed with error: " << strerror(err); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } const int xauthHandle = m_xauthFile.handle(); if (xauthHandle != -1 && fchown(xauthHandle, pw.pw_uid, pw.pw_gid) != 0) { qCritical() << "fchown failed for" << m_xauthFile.fileName(); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } -#if defined(Q_OS_FREEBSD) - // execve() uses the environment prepared in Backend::openSession(), - // therefore environment variables which are set here are ignored. - if (setusercontext(NULL, &pw, pw.pw_uid, LOGIN_SETALL) != 0) { - qCritical() << "setusercontext(NULL, *, " << pw.pw_uid << ", LOGIN_SETALL) failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); - } -#else if (setgid(pw.pw_gid) != 0) { qCritical() << "setgid(" << pw.pw_gid << ") failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } // fetch ambient groups from PAM's environment; @@ -310,7 +245,7 @@ namespace DDM { if ((n_pam_groups = getgroups(n_pam_groups, pam_groups)) == -1) { qCritical() << "getgroups() failed to fetch supplemental" << "PAM groups for user:" << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } } else { n_pam_groups = 0; @@ -327,7 +262,7 @@ namespace DDM { &n_user_groups)) == -1 ) { qCritical() << "getgrouplist(" << pw.pw_name << ", " << pw.pw_gid << ") failed"; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } } @@ -343,7 +278,7 @@ namespace DDM { // setgroups(2) handles duplicate groups if (setgroups(n_groups, groups) != 0) { qCritical() << "setgroups() failed for user: " << username; - exit (Auth::HELPER_OTHER_ERROR); + exit (Auth::OTHER_ERROR); } delete[] groups; } @@ -352,13 +287,13 @@ namespace DDM { if (setuid(pw.pw_uid) != 0) { qCritical() << "setuid(" << pw.pw_uid << ") failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } -#endif /* Q_OS_FREEBSD */ + if (chdir(pw.pw_dir) != 0) { qCritical() << "chdir(" << pw.pw_dir << ") failed for user: " << username; qCritical() << "verify directory exist and has sufficient permissions"; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } if (sessionClass != QLatin1String("greeter")) { @@ -397,9 +332,4 @@ namespace DDM { } } } - - qint64 UserSession::cachedProcessId() { - return m_cachedProcessId; - } - } diff --git a/src/helper/UserSession.h b/src/daemon/UserSession.h similarity index 65% rename from src/helper/UserSession.h rename to src/daemon/UserSession.h index f6fd625..8cb2599 100644 --- a/src/helper/UserSession.h +++ b/src/daemon/UserSession.h @@ -27,50 +27,29 @@ #include namespace DDM { - class HelperApp; + class Auth; class XOrgUserHelper; class WaylandHelper; class UserSession : public QProcess { Q_OBJECT public: - explicit UserSession(HelperApp *parent); + explicit UserSession(Auth *parent); - bool start(); + void start(); void stop(); - QString displayServerCommand() const; - void setDisplayServerCommand(const QString &command); - - void setPath(const QString &path); - QString path() const; - - /*! - \brief Gets m_cachedProcessId - \return The cached process ID - */ - qint64 cachedProcessId(); - - - Q_SIGNALS: - void finished(int exitCode); + /** + * Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout + */ + qint64 cachedProcessId = -1; private: - void setup(); - // Don't call it directly, it will be invoked by the child process only void childModifier(); - QString m_path { }; QTemporaryFile m_xauthFile; - QString m_displayServerCmd; - - HelperApp *m_helperApp; - - /*! - Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout - */ - qint64 m_cachedProcessId = -1; + Auth *m_auth; }; } diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt deleted file mode 100644 index d877f7d..0000000 --- a/src/helper/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -include(CheckLibraryExists) - -include_directories( - ${LIBXAU_INCLUDE_DIRS} -) - -set(HELPER_SOURCES - HelperApp.cpp - UserSession.cpp - Pam.cpp -) - -add_executable(ddm-helper ${HELPER_SOURCES}) -target_link_libraries(ddm-helper - PRIVATE - auth - ${PAM_LIBRARIES} - PUBLIC - Qt${QT_MAJOR_VERSION}::Network - Qt${QT_MAJOR_VERSION}::DBus - Qt${QT_MAJOR_VERSION}::Qml -) -if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - # On FreeBSD (possibly other BSDs as well), we want to use - # setusercontext() to set up the login configuration from login.conf - find_library(_have_libutil util) - if(_have_libutil) - check_library_exists(${_have_libutil} setusercontext "" _have_setusercontext) - endif() - if(_have_libutil AND _have_setusercontext) - target_link_libraries(ddm-helper PUBLIC ${_have_libutil}) - endif() -endif() - -install(TARGETS ddm-helper RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-wayland HelperStartWayland.cpp waylandsocketwatcher.cpp waylandhelper.cpp) -target_link_libraries(ddm-helper-start-wayland - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-single-wayland HelperStartSingleMode.cpp singlewaylandhelper.cpp) -target_link_libraries(ddm-helper-start-single-wayland - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-single-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-x11user HelperStartX11User.cpp xorguserhelper.cpp) -target_link_libraries(ddm-helper-start-x11user - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-x11user RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -if(JOURNALD_FOUND) - target_link_libraries(ddm-helper - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-x11user - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-wayland - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-single-wayland - PUBLIC - ${JOURNALD_LIBRARIES} - ) -endif() diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp deleted file mode 100644 index 1fe0f5f..0000000 --- a/src/helper/HelperApp.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Main authentication application class - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "HelperApp.h" -#include "Pam.h" -#include "Configuration.h" -#include "UserSession.h" -#include "SafeDataStream.h" - -#include "MessageHandler.h" -#include "VirtualTerminal.h" -#include "SignalHandler.h" -#include "Messages.h" -#include "SafeDataStream.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(Q_OS_LINUX) -#include -#endif -#include -#include -#include - -namespace DDM { - static Request passwordRequest{ - { { AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password: "), true } } - }; - - HelperApp::HelperApp(int& argc, char** argv) - : QCoreApplication(argc, argv) - , m_pam(new Pam(this)) - , m_session(new UserSession(this)) - , m_socket(new QLocalSocket(this)) { - qInstallMessageHandler(HelperMessageHandler); - //TODO: replace this workaround in the future - - QTimer::singleShot(0, this, SLOT(setUp())); - } - - void HelperApp::setUp() { - const QStringList args = QCoreApplication::arguments(); - QString server; - int pos; - - if ((pos = args.indexOf(QStringLiteral("--socket"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - server = args[pos + 1]; - } - - if ((pos = args.indexOf(QStringLiteral("--id"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_id = QString(args[pos + 1]).toLongLong(); - } - - if ((pos = args.indexOf(QStringLiteral("--start"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_session->setPath(args[pos + 1]); - } - - if ((pos = args.indexOf(QStringLiteral("--user"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_user = args[pos + 1]; - m_pam->user = m_user; - } - - if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_session->setDisplayServerCommand(args[pos + 1]); - } - - if ((pos = args.indexOf(QStringLiteral("--identify-only"))) >= 0) { - m_identifyOnly = true; - } - - if ((pos = args.indexOf(QStringLiteral("--skip-auth"))) >= 0) { - m_skipAuth = true; - } - - if (server.isEmpty() || m_id <= 0) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - - connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); - if(!m_identifyOnly){ - connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); - } - - connect(new SignalHandler(this), &SignalHandler::sigtermReceived, this, [] { - qDebug() << "sigterm received."; - qApp->quit(); - }); - - m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered); - } - - void HelperApp::doAuth() { - SafeDataStream str(m_socket); - str << Msg::HELLO << m_id; - str.send(); - if (str.status() != QDataStream::Ok) - qCritical() << "Couldn't write initial message:" << str.status(); - - if (!m_pam->start()) { - authenticated(QString()); - - // write failed login to btmp - const QProcessEnvironment env = m_session->processEnvironment(); - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - utmpLogin(vt, displayId, m_user, 0, false); - - exit(Auth::HELPER_AUTH_ERROR); - return; - } - - Q_ASSERT(getuid() == 0); - if (!m_skipAuth) { - Request req = request(passwordRequest); - if (req.prompts.length() <= 0 || !m_pam->authenticate(req.prompts[0].response)) { - authenticated(QString()); - - // write failed login to btmp - const QProcessEnvironment env = m_session->processEnvironment(); - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - utmpLogin(vt, displayId, m_user, 0, false); - - exit(Auth::HELPER_AUTH_ERROR); - return; - } - } - - QProcessEnvironment env = authenticated(m_user); - - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - // Qt internally may load the xdg portal system early on, prevent this, we do not have a functional session running. - env.insert(QStringLiteral("QT_NO_XDG_DESKTOP_PORTAL"), QStringLiteral("1")); - for (const auto &entry : mainConfig.GreeterEnvironment.get()) { - const int index = entry.indexOf(QLatin1Char('=')); - if (index < 0) { - qWarning() << "Malformed environment variable" << entry; - continue; - } - env.insert(entry.left(index), entry.mid(index + 1)); - } - } - - if (!m_session->path().isEmpty()) { - env.insert(m_session->processEnvironment()); - m_session->setProcessEnvironment(env); - - auto sessionEnv = m_pam->openSession(env); - if (!sessionEnv.has_value()) { - sessionOpened(false, 0); - exit(Auth::HELPER_SESSION_ERROR); - return; - } - - env = *sessionEnv; - int sessionId = env.value(QStringLiteral("XDG_SESSION_ID")).toInt(); - sessionOpened(true, sessionId); - - struct passwd *pw; - pw = getpwnam(qPrintable(m_user)); - if (pw) { - env.insert(QStringLiteral("HOME"), QString::fromLocal8Bit(pw->pw_dir)); - env.insert(QStringLiteral("PWD"), QString::fromLocal8Bit(pw->pw_dir)); - env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); - env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); - env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); - } - m_session->setProcessEnvironment(env); - m_session->start(); - - // write successful login to utmp/wtmp - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { - // cache pid for session end - utmpLogin(vt, displayId, m_user, m_session->processId(), true); - } - } - else - exit(Auth::HELPER_SUCCESS); - } - - void HelperApp::sessionFinished(int status) { - exit(status); - } - - void HelperApp::info(const QString& message, Auth::Info type) { - SafeDataStream str(m_socket); - str << Msg::INFO << message << type; - str.send(); - m_socket->waitForBytesWritten(); - } - - void HelperApp::error(const QString& message, Auth::Error type) { - SafeDataStream str(m_socket); - str << Msg::ERROR << message << type; - str.send(); - m_socket->waitForBytesWritten(); - } - - bool HelperApp::isSingleMode() const { - const QStringList args = QCoreApplication::arguments(); - return args.indexOf(QStringLiteral("--single-mode")) >= 0; - } - - bool HelperApp::isGreeter() const { - const QStringList args = QCoreApplication::arguments(); - return args.indexOf(QStringLiteral("--greeter")) >= 0; - } - - Request HelperApp::request(const Request& request) { - Msg m = Msg::MSG_UNKNOWN; - Request response; - SafeDataStream str(m_socket); - str << Msg::REQUEST << request; - str.send(); - str.receive(); - str >> m >> response; - if (m != REQUEST) { - response = Request(); - qCritical() << "Received a wrong opcode instead of REQUEST:" << m; - } - return response; - } - - QProcessEnvironment HelperApp::authenticated(const QString &user) { - Msg m = Msg::MSG_UNKNOWN; - QProcessEnvironment env; - SafeDataStream str(m_socket); - str << Msg::AUTHENTICATED << user; - str.send(); - if (user.isEmpty()) - return env; - str.receive(); - str >> m >> env >> m_cookie; - if (m != AUTHENTICATED) { - env = QProcessEnvironment(); - m_cookie = {}; - qCritical() << "Received a wrong opcode instead of AUTHENTICATED:" << m; - } - return env; - } - - void HelperApp::sessionOpened(bool success, int sessionId) { - Msg m = Msg::MSG_UNKNOWN; - SafeDataStream str(m_socket); - str << Msg::SESSION_STATUS << success << sessionId; - str.send(); - str.receive(); - str >> m; - if (m != SESSION_STATUS) { - qCritical() << "Received a wrong opcode instead of SESSION_STATUS:" << m; - } - } - - void HelperApp::displayServerStarted(const QString &displayName) - { - Msg m = Msg::MSG_UNKNOWN; - SafeDataStream str(m_socket); - str << Msg::DISPLAY_SERVER_STARTED << displayName; - str.send(); - str.receive(); - str >> m; - if (m != DISPLAY_SERVER_STARTED) { - qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m; - } - } - - UserSession *HelperApp::session() { - return m_session; - } - - const QString& HelperApp::user() const { - return m_user; - } - - const QByteArray& HelperApp::cookie() const { - return m_cookie; - } - - HelperApp::~HelperApp() { - Q_ASSERT(getuid() == 0); - - m_session->stop(); - if(!m_identifyOnly && m_pam->sessionOpened){ - m_pam->closeSession(); - } - - // write logout to utmp/wtmp - qint64 pid = m_session->cachedProcessId(); - if (pid < 0) { - return; - } - QProcessEnvironment env = m_session->processEnvironment(); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { - QString vt = env.value(QStringLiteral("XDG_VTNR")); - QString displayId = env.value(QStringLiteral("DISPLAY")); - utmpLogout(vt, displayId, pid); - } - } - - void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { - struct utmpx entry { }; - struct timeval tv; - - entry.ut_type = USER_PROCESS; - entry.ut_pid = pid; - - // ut_line: vt - if (!vt.isEmpty()) { - QString tty = QStringLiteral("tty"); - tty.append(vt); - QByteArray ttyBa = tty.toLocal8Bit(); - const char* ttyChar = ttyBa.constData(); - strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); - } - - // ut_host: displayName - QByteArray displayBa = displayName.toLocal8Bit(); - const char* displayChar = displayBa.constData(); - strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); - - // ut_user: user - QByteArray userBa = user.toLocal8Bit(); - const char* userChar = userBa.constData(); - strncpy(entry.ut_user, userChar, sizeof(entry.ut_user) -1); - - gettimeofday(&tv, NULL); - entry.ut_tv.tv_sec = tv.tv_sec; - entry.ut_tv.tv_usec = tv.tv_usec; - - // write to utmp - setutxent(); - if (!pututxline (&entry)) - qWarning() << "Failed to write utmpx: " << strerror(errno); - endutxent(); - -#if !defined(Q_OS_FREEBSD) - // append to failed login database btmp - if (!authSuccessful) { -#if defined(Q_OS_LINUX) - updwtmpx("/var/log/btmp", &entry); -#endif - } - - // append to wtmp - else { -#if defined(Q_OS_LINUX) - updwtmpx("/var/log/wtmp", &entry); -#endif - } -#endif - } - - void HelperApp::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { - struct utmpx entry { }; - struct timeval tv; - - entry.ut_type = DEAD_PROCESS; - entry.ut_pid = pid; - - // ut_line: vt - if (!vt.isEmpty()) { - QString tty = QStringLiteral("tty"); - tty.append(vt); - QByteArray ttyBa = tty.toLocal8Bit(); - const char* ttyChar = ttyBa.constData(); - strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); - } - - // ut_host: displayName - QByteArray displayBa = displayName.toLocal8Bit(); - const char* displayChar = displayBa.constData(); - strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); - - gettimeofday(&tv, NULL); - entry.ut_tv.tv_sec = tv.tv_sec; - entry.ut_tv.tv_usec = tv.tv_usec; - - // write to utmp - setutxent(); - if (!pututxline (&entry)) - qWarning() << "Failed to write utmpx: " << strerror(errno); - endutxent(); - -#if defined(Q_OS_LINUX) - // append to wtmp - updwtmpx("/var/log/wtmp", &entry); -#elif defined(Q_OS_FREEBSD) - pututxline(&entry); -#endif - } -} - -int main(int argc, char** argv) { - DDM::HelperApp app(argc, argv); - return app.exec(); -} diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h deleted file mode 100644 index e88ab81..0000000 --- a/src/helper/HelperApp.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Main authentication application class - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef Auth_H -#define Auth_H - -#include -#include - -#include "AuthMessages.h" - -class QLocalSocket; - -namespace DDM { - class Pam; - class UserSession; - class HelperApp : public QCoreApplication - { - Q_OBJECT - public: - HelperApp(int& argc, char** argv); - virtual ~HelperApp(); - - UserSession *session(); - const QString &user() const; - const QByteArray &cookie() const; - - bool isSingleMode() const; - bool isGreeter() const; - - public slots: - Request request(const Request &request); - void info(const QString &message, Auth::Info type); - void error(const QString &message, Auth::Error type); - QProcessEnvironment authenticated(const QString &user); - void displayServerStarted(const QString &displayName); - void sessionOpened(bool success, int sessionId); - - private slots: - void setUp(); - void doAuth(); - - void sessionFinished(int status); - - private: - qint64 m_id { -1 }; - Pam *m_pam { nullptr }; - UserSession *m_session { nullptr }; - QLocalSocket *m_socket { nullptr }; - QString m_user { }; - // TODO: get rid of this in a nice clean way along the way with moving to user session X server - QByteArray m_cookie { }; - bool m_skipAuth = false; - bool m_identifyOnly = false; - - /*! - \brief Write utmp/wtmp/btmp records when a user logs in - \param vt Virtual terminal (tty7, tty8,...) - \param displayName Display (:0, :1,...) - \param user User logging in - \param pid User process ID (e.g. PID of startkde) - \param authSuccessful Was authentication successful - */ - void utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful); - - /*! - \brief Write utmp/wtmp records when a user logs out - \param vt Virtual terminal (tty7, tty8,...) - \param displayName Display (:0, :1,...) - \param pid User process ID (e.g. PID of startkde) - */ - void utmpLogout(const QString &vt, const QString &displayName, qint64 pid); - }; -} - -#endif // Auth_H diff --git a/src/helper/HelperStartSingleMode.cpp b/src/helper/HelperStartSingleMode.cpp deleted file mode 100644 index 5f9344c..0000000 --- a/src/helper/HelperStartSingleMode.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include - -#include "singlewaylandhelper.h" -#include "MessageHandler.h" -#include -#include "Auth.h" -#include "SignalHandler.h" - -void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(WaylandHelperMessageHandler); - QCoreApplication app(argc, argv); - using namespace DDM; - DDM::SignalHandler s; - - //Q_ASSERT(::getuid() != 0); - if (argc < 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return Auth::HELPER_OTHER_ERROR; - } - - SingleWaylandHelper helper; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::exit(0); - }); - - QStringList args; - args += app.arguments()[1].split(" "); - args += app.arguments()[2].split(" "); - - if (!helper.start(args.takeFirst(), args.join(" "))) { - qWarning() << "DDM was unable to start" << app.arguments(); - return Auth::HELPER_DISPLAYSERVER_ERROR; - } - - return app.exec(); -} diff --git a/src/helper/HelperStartWayland.cpp b/src/helper/HelperStartWayland.cpp deleted file mode 100644 index 6043d3d..0000000 --- a/src/helper/HelperStartWayland.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Session process wrapper - * Copyright (C) 2021 Aleix Pol Gonzalez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -/** - * This application sole purpose is to launch a wayland compositor (first - * argument) and as soon as it's set up to launch a client (second argument) - */ - -#include -#include -#include -#include -#include -#include - -#include "waylandhelper.h" -#include "MessageHandler.h" -#include -#include "Auth.h" -#include "SignalHandler.h" - -void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(WaylandHelperMessageHandler); - QCoreApplication app(argc, argv); - using namespace DDM; - DDM::SignalHandler s; - - Q_ASSERT(::getuid() != 0); - if (argc < 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return Auth::HELPER_OTHER_ERROR; - } - - WaylandHelper helper; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::exit(0); - }); - QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { - qDebug("quitting helper-start-wayland"); - helper.stop(); - }); - QObject::connect(&helper, &WaylandHelper::failed, &app, [&app] { - QTextStream(stderr) << "Failed to start wayland session" << Qt::endl; - app.exit(Auth::HELPER_SESSION_ERROR); - }); - - if (!helper.startCompositor(app.arguments()[1])) { - qWarning() << "DDM was unable to start" << app.arguments()[1]; - return Auth::HELPER_DISPLAYSERVER_ERROR; - } - - helper.startGreeter(app.arguments()[2]); - - return app.exec(); -} diff --git a/src/helper/HelperStartX11User.cpp b/src/helper/HelperStartX11User.cpp deleted file mode 100644 index 6f3a8c4..0000000 --- a/src/helper/HelperStartX11User.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Session process wrapper - * Copyright (C) 2021 Aleix Pol Gonzalez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -/** - * This application sole purpose is to launch an X11 rootless compositor compositor (first - * argument) and as soon as it's set up to launch a client (second argument) - */ - -#include -#include -#include -#include -#include -#include "xorguserhelper.h" -#include "MessageHandler.h" -#include -#include "SignalHandler.h" - -void X11UserHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("X11UserHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(X11UserHelperMessageHandler); - QCoreApplication app(argc, argv); - DDM::SignalHandler s; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::instance()->exit(-1); - }); - - Q_ASSERT(::getuid() != 0); - if (argc != 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return 33; - } - - using namespace DDM; - XOrgUserHelper helper; - QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { - qDebug("quitting helper-start-x11"); - helper.stop(); - }); - QObject::connect(&helper, &XOrgUserHelper::displayChanged, &app, [&helper, &app] { - qDebug() << "starting XOrg Greeter..." << helper.sessionEnvironment().value(QStringLiteral("DISPLAY")); - auto args = QProcess::splitCommand(app.arguments()[2]); - - QProcess *process = new QProcess(&app); - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->setProgram(args.takeFirst()); - process->setArguments(args); - process->setProcessEnvironment(helper.sessionEnvironment()); - process->start(); - QObject::connect(process, QOverload::of(&QProcess::finished), &app, &QCoreApplication::quit); - }); - - helper.start(app.arguments()[1]); - return app.exec(); -} diff --git a/src/helper/singlewaylandhelper.cpp b/src/helper/singlewaylandhelper.cpp deleted file mode 100644 index 3320e2c..0000000 --- a/src/helper/singlewaylandhelper.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "singlewaylandhelper.h" - -#include -#include -#include -#include - -SingleWaylandHelper::SingleWaylandHelper(QObject *parent) - : QObject(parent) -{} - -bool SingleWaylandHelper::start(const QString &compositor, const QString &cmd) -{ - auto args = QProcess::splitCommand(cmd); - - m_process = new QProcess(this); - m_process->setProgram(compositor); - m_process->setArguments(args); - m_process->setStandardOutputFile(QProcess::nullDevice()); - m_process->setStandardErrorFile(QProcess::nullDevice()); - m_process->setStandardInputFile(QProcess::nullDevice()); - m_process->setProcessEnvironment([]{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert("LIBSEAT_BACKEND", "seatd"); - env.insert("DSG_APP_ID", "org.deepin.dde.treeland"); - return env; - }()); - - connect(m_process, QOverload::of(&QProcess::finished), - qApp, &QCoreApplication::quit); - - m_process->start(); - if (!m_process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(m_process->errorString())); - return false; - } - - return true; -} diff --git a/src/helper/singlewaylandhelper.h b/src/helper/singlewaylandhelper.h deleted file mode 100644 index 838edc9..0000000 --- a/src/helper/singlewaylandhelper.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -class QProcess; - -class SingleWaylandHelper : public QObject { - Q_OBJECT -public: - explicit SingleWaylandHelper(QObject *parent = nullptr); - - bool start(const QString& compositor, const QString& args); - -private: - QProcess *m_process; -}; diff --git a/src/helper/waylandhelper.cpp b/src/helper/waylandhelper.cpp deleted file mode 100644 index 4a4acfc..0000000 --- a/src/helper/waylandhelper.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "Configuration.h" - -#include "waylandhelper.h" -#include "waylandsocketwatcher.h" -#include "VirtualTerminal.h" - -#include -#include - -namespace DDM { - -WaylandHelper::WaylandHelper(QObject *parent) - : QObject(parent) - , m_environment(QProcessEnvironment::systemEnvironment()) - , m_watcher(new WaylandSocketWatcher(this)) -{ -} - -bool WaylandHelper::startCompositor(const QString &cmd) -{ - m_watcher->start(); - return startProcess(cmd, &m_serverProcess); -} - -void stopProcess(QProcess *process) -{ - if (process && process->state() != QProcess::NotRunning) { - qInfo() << "Stopping..." << process->program(); - process->terminate(); - if (!process->waitForFinished(5000)) { - process->kill(); - process->waitForFinished(25000); - } - process->deleteLater(); - process = nullptr; - } -} - -void WaylandHelper::stop() -{ - m_watcher->stop(); - stopProcess(m_greeterProcess); - stopProcess(m_serverProcess); -} - -bool WaylandHelper::startProcess(const QString &cmd, QProcess **p) -{ - auto *process = new QProcess(this); - process->setProcessEnvironment(m_environment); - process->setInputChannelMode(QProcess::ForwardedInputChannel); - connect(process, &QProcess::readyReadStandardError, this, [process] { - qWarning() << process->readAllStandardError(); - }); - connect(process, &QProcess::readyReadStandardOutput, this, [process] { - qInfo() << process->readAllStandardOutput(); - }); - qDebug() << "Starting Wayland process" << cmd << m_environment.value(QStringLiteral("USER")); - connect(process, QOverload::of(&QProcess::finished), - process, [](int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "wayland compositor finished" << exitCode << exitStatus; - if (exitCode != 0 || exitStatus != QProcess::NormalExit) - QCoreApplication::instance()->quit(); - }); - - auto args = QProcess::splitCommand(cmd); - const auto program = args.takeFirst(); - process->start(program, args); - if (!process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(process->errorString())); - return false; - } - - if (p) - *p = process; - - qDebug() << "started succesfully" << cmd; - return true; -} - -void WaylandHelper::startGreeter(const QString &cmd) -{ - auto args = QProcess::splitCommand(cmd); - - m_greeterProcess = new QProcess(this); - m_greeterProcess->setProgram(args.takeFirst()); - m_greeterProcess->setArguments(args); - connect(m_greeterProcess, &QProcess::readyReadStandardError, this, [this] { - qWarning() << m_greeterProcess->readAllStandardError(); - }); - connect(m_greeterProcess, &QProcess::readyReadStandardOutput, this, [this] { - qInfo() << m_greeterProcess->readAllStandardOutput(); - }); - connect(m_greeterProcess, QOverload::of(&QProcess::finished), - m_greeterProcess, [](int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "wayland greeter finished" << exitCode << exitStatus; - QCoreApplication::instance()->quit(); - }); - - if (m_watcher->status() == WaylandSocketWatcher::Started) { - m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); - m_greeterProcess->setProcessEnvironment(m_environment); - m_greeterProcess->start(); - } else if (m_watcher->status() == WaylandSocketWatcher::Failed) { - Q_EMIT failed(); - } else { - connect(m_watcher, &WaylandSocketWatcher::failed, this, &WaylandHelper::failed); - connect(m_watcher, &WaylandSocketWatcher::started, this, [this] { - m_watcher->stop(); - m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); - m_greeterProcess->setProcessEnvironment(m_environment); - m_greeterProcess->start(); - }); - } -} - -} // namespace DDM diff --git a/src/helper/waylandhelper.h b/src/helper/waylandhelper.h deleted file mode 100644 index ad5b056..0000000 --- a/src/helper/waylandhelper.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef WAYLANDHELPER_H -#define WAYLANDHELPER_H - -#include - -namespace DDM { -class WaylandSocketWatcher; - -class WaylandHelper : public QObject -{ - Q_OBJECT -public: - explicit WaylandHelper(QObject *parent = nullptr); - - bool startCompositor(const QString &cmd); - void startGreeter(const QString &cmd); - void stop(); - -Q_SIGNALS: - void failed(); - -private: - QProcessEnvironment m_environment; - QProcess *m_serverProcess = nullptr; - QProcess *m_greeterProcess = nullptr; - WaylandSocketWatcher * const m_watcher; - - bool startProcess(const QString &cmd, QProcess **p = nullptr); -}; - -} // namespace DDM - - - -#endif diff --git a/src/helper/waylandsocketwatcher.cpp b/src/helper/waylandsocketwatcher.cpp deleted file mode 100644 index 82cf2cd..0000000 --- a/src/helper/waylandsocketwatcher.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* Copyright (C) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "waylandsocketwatcher.h" - -namespace DDM { - -WaylandSocketWatcher::WaylandSocketWatcher(QObject *parent ) - : QObject(parent) - , m_runtimeDir(QDir(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))) -{ - m_runtimeDir.setFilter(QDir::Files | QDir::System); - m_runtimeDir.setNameFilters(QStringList() << QLatin1String("wayland-?")); -} - -WaylandSocketWatcher::Status WaylandSocketWatcher::status() const -{ - return m_status; -} - -QString WaylandSocketWatcher::socketName() const -{ - return m_socketName; -} - -void WaylandSocketWatcher::start() -{ - m_watcher = new QFileSystemWatcher(this); - - // Give the compositor some time to start - m_timer.setSingleShot(true); - m_timer.setInterval(15000); - connect(&m_timer, &QTimer::timeout, this, [this] { - // Time is up and a socket was not found - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - qWarning("Wayland socket watcher timed out"); - m_status = Failed; - Q_EMIT failed(); - }); - - // Check if the socket exists - connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, - [this](const QString &path) { - qDebug() << "Directory" << path << "has changed, checking for Wayland socket"; - - m_runtimeDir.refresh(); - const QFileInfoList fileInfoList = m_runtimeDir.entryInfoList(); - for (const QFileInfo &fileInfo : fileInfoList) { - if (fileInfo.ownerId() == ::getuid()) { - qDebug() << "Found Wayland socket" << fileInfo.absoluteFilePath(); - m_timer.stop(); - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - m_socketName = fileInfo.fileName(); - m_status = Started; - Q_EMIT started(); - break; - } - } - }); - - // Watch for runtime directory changes - if (!m_runtimeDir.exists() || !m_watcher->addPath(m_runtimeDir.absolutePath())) { - qWarning("Cannot watch directory \"%s\" for Wayland socket", - qPrintable(m_runtimeDir.absolutePath())); - m_watcher->deleteLater(); - m_status = Failed; - Q_EMIT failed(); - } - - // Start - m_timer.start(); -} - -void WaylandSocketWatcher::stop() -{ - m_timer.stop(); - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - m_watcher.clear(); - m_status = Stopped; - Q_EMIT stopped(); -} - -} // namespace DDM diff --git a/src/helper/waylandsocketwatcher.h b/src/helper/waylandsocketwatcher.h deleted file mode 100644 index 17cab52..0000000 --- a/src/helper/waylandsocketwatcher.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef WAYLANDSOCKETWATCHER_H -#define WAYLANDSOCKETWATCHER_H - -#include -#include -#include -#include - -namespace DDM { - -class WaylandSocketWatcher : public QObject -{ - Q_OBJECT -public: - enum Status { - Started, - Stopped, - Failed - }; - Q_ENUM(Status) - - explicit WaylandSocketWatcher(QObject *parent = nullptr); - - Status status() const; - QString socketName() const; - - void start(); - void stop(); - -Q_SIGNALS: - void started(); - void stopped(); - void failed(); - -private: - Status m_status = Stopped; - QDir m_runtimeDir; - QString m_socketName; - QTimer m_timer; - QPointer m_watcher; -}; - -} // namespace DDM - -#endif // WAYLANDSOCKETWATCHER_H diff --git a/src/helper/xorguserhelper.cpp b/src/helper/xorguserhelper.cpp deleted file mode 100644 index 919dd2c..0000000 --- a/src/helper/xorguserhelper.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "Configuration.h" - -#include "xorguserhelper.h" - -#include -#include - -namespace DDM { - -XOrgUserHelper::XOrgUserHelper(QObject *parent) - : QObject(parent) -{ -} - -QProcessEnvironment XOrgUserHelper::sessionEnvironment() const -{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display); - env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); - env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); - return env; -} - -QString XOrgUserHelper::display() const -{ - return m_display; -} - -bool XOrgUserHelper::start(const QString &cmd) -{ - // Create xauthority - m_xauth.setAuthDirectory(qEnvironmentVariable("XDG_RUNTIME_DIR")); - m_xauth.setup(); - - // Start server process - if (!startServer(cmd)) - return false; - - // Setup display - startDisplayCommand(); - - return true; -} - -void XOrgUserHelper::stop() -{ - if (m_serverProcess) { - qInfo("Stopping server..."); - m_serverProcess->terminate(); - if (!m_serverProcess->waitForFinished(5000)) { - m_serverProcess->kill(); - m_serverProcess->waitForFinished(25000); - } - m_serverProcess->deleteLater(); - m_serverProcess = nullptr; - - displayFinished(); - } -} - -bool XOrgUserHelper::startProcess(const QString &cmd, - const QProcessEnvironment &env, - QProcess **p) -{ - auto args = QProcess::splitCommand(cmd); - const auto program = args.takeFirst(); - - // Make sure to forward the input of this process into the Xorg - // server, otherwise it will complain that only console users are allowed - auto *process = new QProcess(this); - process->setProcessEnvironment(env); - process->setInputChannelMode(QProcess::ForwardedInputChannel); - connect(process, &QProcess::readyReadStandardError, this, [process] { - qWarning() << process->readAllStandardError(); - }); - connect(process, &QProcess::readyReadStandardOutput, this, [process] { - qInfo() << process->readAllStandardOutput(); - }); - connect(process, QOverload::of(&QProcess::finished), - process, [](int exitCode, QProcess::ExitStatus exitStatus) { - if (exitCode != 0 || exitStatus != QProcess::NormalExit) - QCoreApplication::instance()->quit(); - }); - - process->start(program, args); - if (!process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(process->errorString())); - return false; - } - - if (p) - *p = process; - - return true; -} - -bool XOrgUserHelper::startServer(const QString &cmd) -{ - QString serverCmd = cmd; - - // Create pipe for communicating with X server - // 0 == read from X, 1 == write to X - int pipeFds[2]; - if (::pipe(pipeFds) != 0) { - qCritical("Could not create pipe to start X server"); - return false; - } - - // Do not leak the read endpoint to the X server process - fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); - - // Server environment - // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges - // under Fedora and all distros that use their patch. - // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch - // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights - auto serverEnv = QProcessEnvironment::systemEnvironment(); - serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1")); - - // Append xauth and display fd to the command - auto args = QStringList() - << QStringLiteral("-auth") << m_xauth.authPath() - << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); - - // Append VT from environment - args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR"))); - - // Command string - serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' ')); - - // Start the server process - qInfo("Running server: %s", qPrintable(serverCmd)); - if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) { - ::close(pipeFds[0]); - return false; - } - - // Close the other side of pipe in our process, otherwise reading - // from it may stuck even X server exit - ::close(pipeFds[1]); - - // Read the display number from the pipe - QFile readPipe; - if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { - qCritical("Failed to open pipe to start X Server"); - ::close(pipeFds[0]); - return false; - } - QByteArray displayNumber = readPipe.readLine(); - if (displayNumber.size() < 2) { - // X server gave nothing (or a whitespace) - qCritical("Failed to read display number from pipe"); - ::close(pipeFds[0]); - return false; - } - displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace - m_display = QString::fromLocal8Bit(displayNumber); - qDebug("X11 display: %s", qPrintable(m_display)); - Q_EMIT displayChanged(m_display); - - // Generate xauthority file - // For the X server's copy, the display number doesn't matter. - // An empty file would result in no access control! - if (!m_xauth.addCookie(m_display)) { - qCritical("Failed to write xauth file"); - return false; - } - - // Close our pipe - ::close(pipeFds[0]); - - return true; -} - -void XOrgUserHelper::startDisplayCommand() -{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display); - env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); - - // Set cursor - qInfo("Setting default cursor..."); - QProcess *setCursor = nullptr; - if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) { - if (!setCursor->waitForFinished(1000)) { - qWarning() << "Could not setup default cursor"; - setCursor->kill(); - } - setCursor->deleteLater(); - } - - // Display setup script - auto cmd = mainConfig.X11.DisplayCommand.get(); - qInfo("Running display setup script: %s", qPrintable(cmd)); - QProcess *displayScript = nullptr; - if (startProcess(cmd, env, &displayScript)) { - if (!displayScript->waitForFinished(30000)) - displayScript->kill(); - displayScript->deleteLater(); - } -} - -void XOrgUserHelper::displayFinished() -{ - auto cmd = mainConfig.X11.DisplayStopCommand.get(); - qInfo("Running display stop script: %s", qPrintable(cmd)); - QProcess *displayStopScript = nullptr; - if (startProcess(cmd, sessionEnvironment(), &displayStopScript)) { - if (!displayStopScript->waitForFinished(5000)) - displayStopScript->kill(); - displayStopScript->deleteLater(); - } -} - -} // namespace DDM diff --git a/src/helper/xorguserhelper.h b/src/helper/xorguserhelper.h deleted file mode 100644 index fe8ff2e..0000000 --- a/src/helper/xorguserhelper.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef XORGUSERHELPER_H -#define XORGUSERHELPER_H - -#include - -#include "XAuth.h" - -namespace DDM { - -class XOrgUserHelper : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString display READ display NOTIFY displayChanged) -public: - explicit XOrgUserHelper(QObject *parent = nullptr); - - /// @returns the system environment plus the variables we need here - QProcessEnvironment sessionEnvironment() const; - - QString display() const; - - bool start(const QString &cmd); - void stop(); - -Q_SIGNALS: - void displayChanged(const QString &display); - -private: - QString m_display = QStringLiteral(":0"); - XAuth m_xauth; - QProcess *m_serverProcess = nullptr; - - bool startProcess(const QString &cmd, const QProcessEnvironment &env, - QProcess **p = nullptr); - bool startServer(const QString &cmd); - void startDisplayCommand(); - void displayFinished(); -}; - -} // namespace DDM - -#endif // XORGUSERHELPER_H