diff --git a/REUSE.toml b/REUSE.toml index f9b8f63..88af67b 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/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/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.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/PowerManager.cpp", "src/daemon/PowerManager.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/VirtualTerminal.cpp", "src/daemon/VirtualTerminal.h", "src/daemon/Utils.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/UserSession.cpp", "src/daemon/UserSession.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h"] +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/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.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/PowerManager.cpp", "src/daemon/PowerManager.h", "src/daemon/SeatManager.cpp", "src/daemon/SeatManager.h", "src/daemon/TreelandConnector.cpp", "src/daemon/TreelandConnector.h", "src/daemon/VirtualTerminal.cpp", "src/daemon/VirtualTerminal.h", "src/daemon/Utils.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/UserSession.cpp", "src/daemon/UserSession.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h"] precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" diff --git a/src/daemon/Auth.cpp b/src/daemon/Auth.cpp index 9aa6654..8fe78c4 100644 --- a/src/daemon/Auth.cpp +++ b/src/daemon/Auth.cpp @@ -218,17 +218,19 @@ namespace DDM { return true; } - int Auth::openSession(const QString &command, - QProcessEnvironment env, - const QByteArray &cookie) { + QString Auth::openSession(const QString &command, + QProcessEnvironment env, + const QByteArray &cookie) { Q_ASSERT(authenticated); int pipefd[2]; if (pipe(pipefd) == -1) { qWarning() << "[Auth] pipe failed:" << strerror(errno); - return -1; + return {}; } + char xdgSessionId[128] = {}; + // Here is most safe place to jump VT VirtualTerminal::jumpToVt(tty, false, false); @@ -239,7 +241,7 @@ namespace DDM { qWarning() << "[Auth] fork failed:" << strerror(errno); close(pipefd[0]); close(pipefd[1]); - return -1; + return {}; } case 0: { // Child (session leader) process @@ -275,57 +277,57 @@ namespace DDM { env = *sessionEnv; // Retrieve XDG_SESSION_ID - xdgSessionId = env.value(QStringLiteral("XDG_SESSION_ID")).toInt(); - if (xdgSessionId <= 0) { - qCritical() << "[SessionLeader] Invalid XDG_SESSION_ID from pam_open_session()"; - exit(1); - } - if (write(pipefd[1], &xdgSessionId, sizeof(int)) != sizeof(int)) { + session = env.value(QStringLiteral("XDG_SESSION_ID")); + QByteArray sessionBa = session.toLocal8Bit(); + strcpy(xdgSessionId, sessionBa.constData()); + if (write(pipefd[1], &xdgSessionId, sizeof(char) * 128) != sizeof(char) * 128) { qCritical() << "[SessionLeader] Failed to write XDG_SESSION_ID to parent process!"; exit(1); } // RUN!!! - UserSession session(this); - session.setProcessEnvironment(env); - session.start(command, type, cookie); - if (!session.waitForStarted()) { + UserSession desktop(this); + desktop.setProcessEnvironment(env); + desktop.start(command, type, cookie); + if (!desktop.waitForStarted()) { qCritical() << "[SessionLeader] Failed to start session process. Exit now."; exit(1); } // Send session PID to parent - sessionPid = session.processId(); + sessionPid = desktop.processId(); if (write(pipefd[1], &sessionPid, sizeof(qint64)) != sizeof(qint64)) { qCritical() << "[SessionLeader] Failed to write session PID to parent process!"; exit(1); } qInfo() << "[SessionLeader] Session started with PID" << sessionPid; - session.waitForFinished(-1); + desktop.waitForFinished(-1); // Handle session end - if (session.exitStatus() == QProcess::CrashExit) { + if (desktop.exitStatus() == QProcess::CrashExit) { qCritical() << "[SessionLeader] Session process crashed. Exit now."; exit(1); } qInfo() << "[SessionLeader] Session process finished with exit code" - << session.exitCode() << ". Exiting."; - exit(session.exitCode()); + << desktop.exitCode() << ". Exiting."; + exit(desktop.exitCode()); } default: { // Parent process close(pipefd[1]); - if (read(pipefd[0], &xdgSessionId, sizeof(int)) < 0) { + if (read(pipefd[0], &xdgSessionId, sizeof(char) * 128) != sizeof(char) * 128) { qWarning() << "[Auth] Failed to read XDG_SESSION_ID from child process:" << strerror(errno); close(pipefd[0]); - return -1; + return {}; } + session = QString::fromLocal8Bit(xdgSessionId); + if (read(pipefd[0], &sessionPid, sizeof(qint64)) < 0) { qWarning() << "[Auth] Failed to read session PID from child process:" << strerror(errno); close(pipefd[0]); - return -1; + return {}; } utmpLogin(true); @@ -339,7 +341,7 @@ namespace DDM { }); sessionOpened = true; - return xdgSessionId; + return session; } } } diff --git a/src/daemon/Auth.h b/src/daemon/Auth.h index e050ab1..0e96d6e 100644 --- a/src/daemon/Auth.h +++ b/src/daemon/Auth.h @@ -42,8 +42,8 @@ namespace DDM { /** Virtual terminal number (e.g. 7 for tty7) */ int tty{ 0 }; - /** Logind session ID (the XDG_SESSION_ID env var). Positive values are valid */ - int xdgSessionId{ 0 }; + /** Logind session ID (the XDG_SESSION_ID env var) */ + QString session{}; /** PID of the session leader. Positive values are valid */ pid_t sessionLeaderPid{ 0 }; @@ -67,11 +67,11 @@ namespace DDM { * @param command Command to execute as user process * @param env Environment variables to set for the session * @param cookie XAuth cookie, must be provided if type=X11 - * @return A valid XDG_SESSION_ID on success, zero or negative on failure + * @return A valid XDG_SESSION_ID on success, empty string on failure */ - int openSession(const QString &command, - QProcessEnvironment env, - const QByteArray &cookie = QByteArray()); + QString openSession(const QString &command, + QProcessEnvironment env, + const QByteArray &cookie = QByteArray()); /** * Close PAM session diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 5347f96..4734023 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,19 +1,19 @@ # Generate treeland-ddm protocol files -set(TREELAND_DDM_HEADER ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v1.h) -set(TREELAND_DDM_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v1.c) +set(TREELAND_DDM_HEADER ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v2.h) +set(TREELAND_DDM_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v2.c) add_custom_command( OUTPUT ${TREELAND_DDM_HEADER} - COMMAND wayland-scanner client-header < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml > ${TREELAND_DDM_HEADER} - DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml - COMMENT "Generating treeland-ddm-v1.h" + COMMAND wayland-scanner client-header < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v2.xml > ${TREELAND_DDM_HEADER} + DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v2.xml + COMMENT "Generating treeland-ddm-v2.h" ) add_custom_command( OUTPUT ${TREELAND_DDM_SOURCE} - COMMAND wayland-scanner private-code < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml > ${TREELAND_DDM_SOURCE} - DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml - COMMENT "Generating treeland-ddm-v1.c" + COMMAND wayland-scanner private-code < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v2.xml > ${TREELAND_DDM_SOURCE} + DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v2.xml + COMMENT "Generating treeland-ddm-v2.c" ) configure_file(config.h.in config.h IMMEDIATE @ONLY) @@ -24,7 +24,6 @@ set(DAEMON_SOURCES DisplayManager.cpp PowerManager.cpp SeatManager.cpp - SocketServer.cpp TreelandConnector.cpp UserSession.cpp VirtualTerminal.cpp diff --git a/src/daemon/DaemonApp.cpp b/src/daemon/DaemonApp.cpp index 4a07e10..696bb25 100644 --- a/src/daemon/DaemonApp.cpp +++ b/src/daemon/DaemonApp.cpp @@ -84,7 +84,6 @@ namespace DDM { connect(m_signalHandler, &SignalHandler::sigintReceived, this, &DaemonApp::quit); connect(m_signalHandler, &SignalHandler::sigtermReceived, this, &DaemonApp::quit); - m_treelandConnector = new TreelandConnector(); // log message qDebug() << "Starting..."; diff --git a/src/daemon/DaemonApp.h b/src/daemon/DaemonApp.h index 5029b74..d5a9dff 100644 --- a/src/daemon/DaemonApp.h +++ b/src/daemon/DaemonApp.h @@ -30,7 +30,6 @@ namespace DDM { class PowerManager; class SeatManager; class SignalHandler; - class TreelandConnector; class DaemonApp : public QCoreApplication { Q_OBJECT @@ -45,7 +44,6 @@ namespace DDM { inline PowerManager *powerManager() const { return m_powerManager; }; inline SeatManager *seatManager() const { return m_seatManager; }; inline SignalHandler *signalHandler() const { return m_signalHandler; }; - inline TreelandConnector *treelandConnector() const { return m_treelandConnector; }; void backToNormal(); @@ -61,7 +59,6 @@ namespace DDM { PowerManager *m_powerManager { nullptr }; SeatManager *m_seatManager { nullptr }; SignalHandler *m_signalHandler { nullptr }; - TreelandConnector *m_treelandConnector { nullptr }; }; } diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index c3af7a5..b72b48a 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -27,8 +27,6 @@ #include "DisplayManager.h" #include "Messages.h" #include "SeatManager.h" -#include "SocketServer.h" -#include "SocketWriter.h" #include "TreelandConnector.h" #include "TreelandDisplayServer.h" #include "XorgDisplayServer.h" @@ -37,10 +35,11 @@ #include "Login1Manager.h" #include "VirtualTerminal.h" +#include "treeland-ddm-v2.h" + #include #include #include -#include #include #include @@ -98,13 +97,13 @@ namespace DDM { Display::Display(SeatManager *parent, QString name) : QObject(parent) - , name(name) - , m_socketServer(new SocketServer(this)) { + , name(name) { // Create display server terminalId = fetchAvailableVt(); qDebug("Using VT %d", terminalId); - m_treeland = new TreelandDisplayServer(m_socketServer, this); + m_treeland = new TreelandDisplayServer(this); + connector = new TreelandConnector(this); // Record current VT as ddm user session DaemonApp::instance()->displayManager()->AddSession( @@ -112,62 +111,20 @@ namespace DDM { name, "dde", static_cast(VirtualTerminal::currentVt())); - - // connect connected signal - connect(m_socketServer, &SocketServer::connected, this, &Display::connected); - - // connect login signal - connect(m_socketServer, &SocketServer::login, this, &Display::login); - - // connect logout signal - connect(m_socketServer, &SocketServer::logout, this, &Display::logout); - - // connect lock signal - connect(m_socketServer, &SocketServer::lock, this, &Display::lock); - - // connect unlock signal - connect(m_socketServer, &SocketServer::unlock,this, &Display::unlock); - - // connect login result signals - connect(this, &Display::loginFailed, m_socketServer, &SocketServer::loginFailed); } Display::~Display() { stop(); } - void Display::activateSession(const QString &user, int xdgSessionId) { - if (xdgSessionId <= 0 && user != QStringLiteral("dde")) { - qCritical() << "Invalid xdg session id" << xdgSessionId << "for user" << user; - return; - } - - m_treeland->activateUser(user, xdgSessionId); - - if (xdgSessionId > 0 && Logind::isAvailable()) { - OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); - manager.ActivateSession(QString::number(xdgSessionId)); - } - } - bool Display::start() { if (m_started) return true; - VirtualTerminal::jumpToVt(terminalId, false); + VirtualTerminal::jumpToVt(terminalId, false, false); if (!m_treeland->start()) return false; - - // start socket server - m_socketServer->start("treeland"); - - // Update dbus info - DaemonApp::instance()->displayManager()->setAuthInfo(m_socketServer->socketAddress()); - - // change the owner and group of the socket to avoid permission denied errors - struct passwd *pw = getpwnam("dde"); - if (pw && chown(qPrintable(m_socketServer->socketAddress()), pw->pw_uid, pw->pw_gid) == -1) - qWarning() << "Failed to change owner of the socket"; + connector->connect(); // set flags m_started = true; @@ -186,12 +143,12 @@ namespace DDM { } auths.clear(); - // stop socket server - m_socketServer->stop(); - if (m_x11Server) m_x11Server->stop(); + // disconnect from treeland + connector->disconnect(); + // stop display server m_treeland->stop(); @@ -202,21 +159,10 @@ namespace DDM { emit stopped(); } - void Display::connected(QLocalSocket *socket) { - // send logged in users (for possible crash recovery) - SocketWriter writer(socket); - for (Auth *auth : std::as_const(auths)) { - if (auth->sessionOpened) - writer << quint32(DaemonMessages::UserLoggedIn) << auth->user << auth->xdgSessionId; - } - } - - void Display::login(QLocalSocket *socket, - const QString &user, const QString &password, - const Session &session) { + void Display::login(const QString &user, const QString &password, const Session &session) { if (user == QLatin1String("dde")) { qWarning() << "Login attempt for user dde"; - emit loginFailed(socket, user); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_USER); return; } @@ -236,20 +182,24 @@ namespace DDM { if (auth->authenticated) { qWarning() << "Existing authentication ongoing, aborting"; + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_EXISTING_AUTHENTICATION_ONGOING); return; } // sanity check if (!session.isValid()) { qCritical() << "Invalid session" << session.fileName(); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_SESSION); return; } if (session.xdgSessionType().isEmpty()) { qCritical() << "Failed to find XDG session type for session" << session.fileName(); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_SESSION); return; } if (session.exec().isEmpty()) { qCritical() << "Failed to find command for session" << session.fileName(); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_SESSION); return; } @@ -259,7 +209,7 @@ namespace DDM { else auth->tty = terminalId; if (!auth->authenticate(password.toLocal8Bit())) { - Q_EMIT loginFailed(socket, user); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_AUTHENTICATION_FAILED); return; } @@ -312,7 +262,7 @@ namespace DDM { auth->type = X11; qInfo() << "Stopping Treeland"; - daemonApp->treelandConnector()->disconnect(); + connector->disconnect(); m_treeland->stop(); QThread::msleep(500); // give some time to treeland to stop properly @@ -332,17 +282,18 @@ namespace DDM { auth->type = Wayland; qInfo() << "Stopping Treeland"; - daemonApp->treelandConnector()->disconnect(); + connector->disconnect(); m_treeland->stop(); QThread::msleep(500); // give some time to treeland to stop properly } // Open Logind session & Exec the desktop process - int xdgSessionId = auth->openSession(session.exec(), env, cookie); + QString xdgSessionId = auth->openSession(session.exec(), env, cookie); - if (xdgSessionId <= 0) { + if (xdgSessionId.isEmpty()) { qCritical() << "Failed to open logind session for user" << user; delete auth; + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INTERNAL_ERROR); return; } @@ -361,8 +312,8 @@ namespace DDM { qInfo() << "Successfully logged in user" << user; } - void Display::logout([[maybe_unused]] QLocalSocket *socket, int id) { - qDebug() << "Logout requested for session id" << id; + void Display::logout(const QString &session) { + qDebug() << "Logout requested for session" << session; // Do not kill the session leader process before // TerminateSession! Logind will only kill the session's // cgroup (session_stop_scope) when the session is not in @@ -373,21 +324,21 @@ namespace DDM { OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); - manager.TerminateSession(QString::number(id)); + manager.TerminateSession(session); } - void Display::lock([[maybe_unused]] QLocalSocket *socket, int id) { - qDebug() << "Lock requested for session id" << id; + void Display::lock(const QString &session) { + qDebug() << "Lock requested for session" << session; OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); - manager.LockSession(QString::number(id)); + manager.LockSession(session); } - void Display::unlock(QLocalSocket *socket, const QString &user, const QString &password) { + void Display::unlock(const QString &user, const QString &password) { if (user == QLatin1String("dde")) { - emit loginFailed(socket, user); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_USER); return; } @@ -399,7 +350,7 @@ namespace DDM { // immediately after use Auth auth(this, user); if (!auth.authenticate(password.toLocal8Bit())) { - Q_EMIT loginFailed(socket, user); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_AUTHENTICATION_FAILED); return; } @@ -413,17 +364,17 @@ namespace DDM { // Find the auth that started the session, which contains full informations for (auto *auth : std::as_const(auths)) { - if (auth->user == user && auth->xdgSessionId > 0) { + if (auth->user == user && !auth->session.isEmpty()) { OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); - manager.UnlockSession(QString::number(auth->xdgSessionId)); - VirtualTerminal::jumpToVt(auth->tty, false); + manager.UnlockSession(auth->session); + VirtualTerminal::jumpToVt(auth->tty, false, false); qInfo() << "Successfully identified user" << user; return; } } qWarning() << "No active session found for user" << user; - Q_EMIT loginFailed(socket, user); + connector->authenticationFailed(TREELAND_DDM_V2_AUTH_ERROR_INVALID_SESSION); } } diff --git a/src/daemon/Display.h b/src/daemon/Display.h index 9027415..c1822c0 100644 --- a/src/daemon/Display.h +++ b/src/daemon/Display.h @@ -28,14 +28,12 @@ #include "Session.h" -class QLocalSocket; - namespace DDM { class Auth; - class XorgDisplayServer; - class TreelandDisplayServer; class SeatManager; - class SocketServer; + class TreelandConnector; + class TreelandDisplayServer; + class XorgDisplayServer; /** Class represents a display (seat) */ class Display : public QObject { @@ -59,17 +57,6 @@ namespace DDM { ~Display(); - /** - * Tell Treeland to activate a certain session. - * - * Called with user = "dde" and xdgSessionId <= 0 - * will send Treeland into lockscreen. - * - * @param user Username - * @param xdgSessionId Logind session ID - */ - void activateSession(const QString &user, int xdgSessionId); - /** Seat name */ QString name{}; @@ -79,6 +66,9 @@ namespace DDM { /** List of active authentications */ QList auths; + /** Treeland connector */ + TreelandConnector *connector{ nullptr }; + public slots: /** * Start the display. @@ -98,25 +88,17 @@ namespace DDM { // Slots for socket to communicate with Treeland // /////////////////////////////////////////////////// - void connected(QLocalSocket *socket); - void login(QLocalSocket *socket, - const QString &user, + void login(const QString &user, const QString &password, const Session &session); - void logout(QLocalSocket *socket, int id); - void lock(QLocalSocket *socket, int id); - void unlock(QLocalSocket *socket, const QString &user, const QString &password); + void logout(const QString &session); + void lock(const QString &session); + void unlock(const QString &session, const QString &password); signals: /** Emitted when stop() */ void stopped(); - ///////////////////////////////////////////////////// - // Signals for socket to communicate with Treeland // - ///////////////////////////////////////////////////// - - void loginFailed(QLocalSocket *socket, const QString &user); - private: /** Indicates whether the display is started */ bool m_started{ false }; @@ -126,9 +108,6 @@ namespace DDM { /** X11 display server, if started */ XorgDisplayServer *m_x11Server{ nullptr }; - - /** Socket server for communication with Treeland */ - SocketServer *m_socketServer { nullptr }; }; } diff --git a/src/daemon/SeatManager.cpp b/src/daemon/SeatManager.cpp index fcd9899..1851db2 100644 --- a/src/daemon/SeatManager.cpp +++ b/src/daemon/SeatManager.cpp @@ -22,6 +22,7 @@ #include "Configuration.h" #include "DaemonApp.h" #include "Display.h" +#include "TreelandConnector.h" #include #include @@ -163,7 +164,7 @@ namespace DDM { for (auto display : std::as_const(displays)) { if (display->name == name) { // switch to greeter - display->activateSession("dde", 0); + display->connector->switchToGreeter(); return; } } diff --git a/src/daemon/SocketServer.cpp b/src/daemon/SocketServer.cpp deleted file mode 100644 index a040b00..0000000 --- a/src/daemon/SocketServer.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2015 Pier Luigi Fiorini -* Copyright (c) 2013 Abdurrahman AVCI -* -* 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 "SocketServer.h" - -#include "DaemonApp.h" -#include "Messages.h" -#include "PowerManager.h" -#include "SocketWriter.h" -#include "TreelandConnector.h" -#include "Utils.h" - -#include - -namespace DDM { - SocketServer::SocketServer(QObject *parent) : QObject(parent) { - } - - QString SocketServer::socketAddress() const { - if (m_server) - return m_server->fullServerName(); - return QString(); - } - - bool SocketServer::start(const QString &displayName) { - // check if the server has been created already - if (m_server) - return false; - - QString socketName = QStringLiteral("ddm-%1-%2").arg(displayName).arg(generateName(6)); - - // log message - qDebug() << "Socket server starting..."; - - // create server - m_server = new QLocalServer(this); - - // set server options - m_server->setSocketOptions(QLocalServer::UserAccessOption); - - // start listening - if (!m_server->listen(socketName)) { - // log message - qCritical() << "Failed to start socket server."; - - // return fail - return false; - } - - - // log message - qDebug() << "Socket server started."; - - // connect signals - connect(m_server, &QLocalServer::newConnection, this, &SocketServer::newConnection); - - // return success - return true; - } - - void SocketServer::stop() { - // check flag - if (!m_server) - return; - - // log message - qDebug() << "Socket server stopping..."; - - // delete server - m_server->deleteLater(); - m_server = nullptr; - - // log message - qDebug() << "Socket server stopped."; - } - - void SocketServer::newConnection() { - // get pending connection - QLocalSocket *socket = m_server->nextPendingConnection(); - - // connect signals - connect(socket, &QLocalSocket::readyRead, this, &SocketServer::readyRead); - connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); - connect(socket, &QLocalSocket::disconnected, this, [this, socket] { - emit disconnected(socket); - }); - } - - void SocketServer::readyRead() { - QLocalSocket *socket = qobject_cast(sender()); - - // check socket - if (!socket) - return; - - // input stream - QDataStream input(socket); - - // Qt's QLocalSocket::readyRead is not designed to be called at every socket.write(), - // so we need to use a loop to read all the signals. - while(socket->bytesAvailable()) { - // read message - quint32 message; - input >> message; - - switch (GreeterMessages(message)) { - case GreeterMessages::Connect: { - // log message - qDebug() << "Message received from greeter: Connect"; - - // Connect wayland socket - QString socketPath; - input >> socketPath; - daemonApp->treelandConnector()->connect(socketPath); - - // send capabilities - SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities()); - - // send host name - SocketWriter(socket) << quint32(DaemonMessages::HostName) << daemonApp->hostName(); - - // emit signal - emit connected(socket); - } - break; - case GreeterMessages::Login: { - // log message - qDebug() << "Message received from greeter: Login"; - - // read username, pasword etc. - QString user, password, filename; - Session session; - input >> user >> password >> session; - - // emit signal - emit login(socket, user, password, session); - } - break; - case GreeterMessages::Logout: { - // log message - qDebug() << "Message received from greeter: Logout"; - // read username - int id; - input >> id; - // emit signal - emit logout(socket, id); - } - break; - case GreeterMessages::Lock : { - // log message - qDebug() << "Message received from greeter: Lock"; - int id; - - input >> id; - emit lock(socket, id); - } - break; - case GreeterMessages::Unlock : { - // log message - qDebug() << "Message received from greeter: Unlock"; - QString user; - QString password; - - input >> user >> password; - emit unlock(socket, user, password); - } - break; - case GreeterMessages::PowerOff: { - // log message - qDebug() << "Message received from greeter: PowerOff"; - - // power off - daemonApp->powerManager()->powerOff(); - } - break; - case GreeterMessages::Reboot: { - // log message - qDebug() << "Message received from greeter: Reboot"; - - // reboot - daemonApp->powerManager()->reboot(); - } - break; - case GreeterMessages::Suspend: { - // log message - qDebug() << "Message received from greeter: Suspend"; - - // suspend - daemonApp->powerManager()->suspend(); - } - break; - case GreeterMessages::Hibernate: { - // log message - qDebug() << "Message received from greeter: Hibernate"; - - // hibernate - daemonApp->powerManager()->hibernate(); - } - break; - case GreeterMessages::HybridSleep: { - // log message - qDebug() << "Message received from greeter: HybridSleep"; - // hybrid sleep - daemonApp->powerManager()->hybridSleep(); - } - break; - case GreeterMessages::BackToNormal: { - // log message - qDebug() << "Message received from greeter: Back to normal"; - // hybrid sleep - daemonApp->backToNormal(); - } - break; - default: { - // log message - qWarning() << "Unknown message" << message; - } - } - } - - } - - void SocketServer::loginFailed(QLocalSocket *socket, const QString &user) { - SocketWriter(socket) << quint32(DaemonMessages::LoginFailed) << user; - } - - void SocketServer::informationMessage(QLocalSocket *socket, const QString &message) { - SocketWriter(socket) << quint32(DaemonMessages::InformationMessage) << message; - } -} diff --git a/src/daemon/SocketServer.h b/src/daemon/SocketServer.h deleted file mode 100644 index e66382d..0000000 --- a/src/daemon/SocketServer.h +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2015 Pier Luigi Fiorini -* Copyright (c) 2013 Abdurrahman AVCI -* -* 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 DDM_SOCKETSERVER_H -#define DDM_SOCKETSERVER_H - -#include -#include - -#include "Session.h" - -class QLocalServer; -class QLocalSocket; - -namespace DDM { - class SocketServer : public QObject { - Q_OBJECT - Q_DISABLE_COPY(SocketServer) - public: - explicit SocketServer(QObject *parent = 0); - - bool start(const QString &sddmName); - void stop(); - - QString socketAddress() const; - - private slots: - void newConnection(); - void readyRead(); - - public slots: - void informationMessage(QLocalSocket *socket, const QString &message); - void loginFailed(QLocalSocket *socket, const QString &user); - - signals: - void login(QLocalSocket *socket, - const QString &user, const QString &password, - const Session &session); - void logout(QLocalSocket *socket, - int id); - void lock(QLocalSocket *socket, - int id); - void unlock(QLocalSocket *socket, - const QString &user, const QString &password); - void connected(QLocalSocket *socket); - void disconnected(QLocalSocket *socket); - - private: - QLocalServer *m_server { nullptr }; - }; -} - -#endif // DDM_SOCKETSERVER_H diff --git a/src/daemon/TreelandConnector.cpp b/src/daemon/TreelandConnector.cpp index 3e3a9ba..d46370c 100644 --- a/src/daemon/TreelandConnector.cpp +++ b/src/daemon/TreelandConnector.cpp @@ -2,292 +2,484 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "TreelandConnector.h" + +// DDM #include "Auth.h" #include "DaemonApp.h" #include "Display.h" #include "DisplayManager.h" +#include "Login1Manager.h" +#include "PowerManager.h" #include "SeatManager.h" +#include "Session.h" #include "VirtualTerminal.h" -#include "treeland-ddm-v1.h" +#include "treeland-ddm-v2.h" -#include -#include -#include +// Qt #include #include +#include +#include +#include +#include -#include -#include +// Wayland +#include + +// System +#include +#include +#include #include #include #include -#include -#include namespace DDM { -// Virtural Terminal helper from VirturalTerminal.h + // Virtural Terminal helper from VirturalTerminal.h -static const char *defaultVtPath = "/dev/tty0"; + static const char *defaultVtPath = "/dev/tty0"; -static bool isVtRunningTreeland(int vtnr) { - for (Display *display : daemonApp->seatManager()->displays) { - if (display->terminalId == vtnr) - return true; - for (Auth *auth : display->auths) - if (auth->tty == vtnr && auth->type == Display::Treeland) + static bool isVtRunningTreeland(int vtnr) { + for (Display *display : daemonApp->seatManager()->displays) { + if (display->terminalId == vtnr) return true; + for (Auth *auth : display->auths) + if (auth->tty == vtnr && auth->type == Display::Treeland) + return true; + } + return false; } - return false; -} - -/** - * Callback function of disableRender - * - * This will be called after treeland render has been disabled, which is - * happened after a VT release-display signal, to finalize VT switching (see - * onReleaseDisplay()). - */ -static void renderDisabled([[maybe_unused]] void *data, struct wl_callback *callback, [[maybe_unused]] uint32_t serial) { - wl_callback_destroy(callback); - - // Acknowledge kernel to release display - int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); - ioctl(fd, VT_RELDISP, 1); - close(fd); - - // Get active VT by reading /sys/class/tty/tty0/active . - // Note that we cannot use open(defaultVtPath, ...) here, as the open() will - // block VT file access, causing error to systemd-getty-generator, and stop - // getty from spawning if current VT is empty. - int activeVt = -1; - QFile tty("/sys/class/tty/tty0/active"); - if (!tty.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning("Failed to open active tty file"); - } else { - auto active = tty.readAll(); - tty.close(); - int scanResult = sscanf(qPrintable(active), "tty%d", &activeVt); - if (scanResult != 1) { - qWarning("Failed to parse active VT from /sys/class/tty/tty0/active with content %s", qPrintable(active)); - activeVt = -1; + + static Display *findSeatOfDevice(QString path) { + // Get the seat of the active VT + sd_device *device = nullptr; + const char *idSeat = nullptr; + auto guard = qScopeGuard([&] { + if (device) + sd_device_unref(device); + if (idSeat) + free((void *)idSeat); + }); + sd_device_new_from_path(&device, qPrintable(path)); + if (!device) { + qWarning() << "Failed to get seat for device" << path; + return nullptr; + } + sd_device_get_property_value(device, "ID_SEAT", &idSeat); + QString idSeatStr = !idSeat || idSeat[0] == '\0' ? "seat0" : QString::fromLocal8Bit(idSeat); + auto res = std::ranges::find_if(daemonApp->seatManager()->displays, [&](const Display *d) { + return d->name == idSeatStr; + }); + return res == daemonApp->seatManager()->displays.end() ? nullptr : *res; + } + + static inline Display *displayForVt(int vtnr) + { + return findSeatOfDevice(VirtualTerminal::path(vtnr)); + } + + /** + * Callback function of disableRender + * + * This will be called after treeland render has been disabled, which is + * happened after a VT release-display signal, to finalize VT switching (see + * onReleaseDisplay()). + */ + static void renderDisabled([[maybe_unused]] void *data, + struct wl_callback *callback, + [[maybe_unused]] uint32_t serial) { + wl_callback_destroy(callback); + + // Acknowledge kernel to release display + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + ioctl(fd, VT_RELDISP, 1); + close(fd); + + // Get active VT by reading /sys/class/tty/tty0/active . + // Note that we cannot use open(defaultVtPath, ...) here, as the open() will + // block VT file access, causing error to systemd-getty-generator, and stop + // getty from spawning if current VT is empty. + int activeVt = -1; + QFile tty("/sys/class/tty/tty0/active"); + if (!tty.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Failed to open active tty file"); + } else { + auto active = tty.readAll(); + tty.close(); + int scanResult = sscanf(qPrintable(active), "tty%d", &activeVt); + if (scanResult != 1) { + qWarning( + "Failed to parse active VT from /sys/class/tty/tty0/active with content %s", + qPrintable(active)); + return; + } + } + + auto display = displayForVt(activeVt); + if (!display) { + qWarning() << "Failed to find seat for active VT" << activeVt; + return; + } + auto conn = display->connector; + auto user = daemonApp->displayManager()->findUserByVt(activeVt); + bool isTreeland = isVtRunningTreeland(activeVt); + qDebug("Next VT: %d, user: %s", activeVt, user.isEmpty() ? "None" : qPrintable(user)); + + if (isTreeland) { + // If user is not empty, it means the switching can be issued by + // ddm-helper. It uses VT signals from VirtualTerminal.h, + // which is not what we want, so we should acquire VT control here. + int activeVtFd = open(defaultVtPath, O_RDWR | O_NOCTTY); + VirtualTerminal::handleVtSwitches(activeVtFd); + close(activeVtFd); + + conn->enableRender(); + conn->switchToUser(user.isEmpty() ? "dde" : user); + } else { + // Switch to a TTY, deactivate treeland session. + conn->switchToGreeter(); + conn->deactivateSession(); } } - auto user = daemonApp->displayManager()->findUserByVt(activeVt); - bool isTreeland = isVtRunningTreeland(activeVt); - auto conn = daemonApp->treelandConnector(); - qDebug("Next VT: %d, user: %s", activeVt, user.isEmpty() ? "None" : qPrintable(user)); - - if (isTreeland) { - // If user is not empty, it means the switching can be issued by - // ddm-helper. It uses VT signals from VirtualTerminal.h, - // which is not what we want, so we should acquire VT control here. - int activeVtFd = open(defaultVtPath, O_RDWR | O_NOCTTY); - VirtualTerminal::handleVtSwitches(activeVtFd); - close(activeVtFd); - - conn->enableRender(); - conn->switchToUser(user.isEmpty() ? "dde" : user); - } else { - // Switch to a TTY, deactivate treeland session. - conn->switchToGreeter(); - conn->deactivateSession(); - } -} - -static const wl_callback_listener renderDisabledListener { - .done = renderDisabled, -}; - -static void onAcquireDisplay() { - int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); - - // Activate treeland session before we acknowledge VT switch. - // This will queue our rendering jobs before any keyboard event, to ensure - // all GUI elements are under a prepared state before next possible VT - // switch issued by keyboard. - int vtnr = VirtualTerminal::getVtActive(fd); - auto user = daemonApp->displayManager()->findUserByVt(vtnr); - auto conn = daemonApp->treelandConnector(); - if (isVtRunningTreeland(vtnr)) { - qDebug("Activate session at VT %d for user %s", vtnr, qPrintable(user)); - conn->activateSession(); - conn->enableRender(); - conn->switchToUser(user); - } - - ioctl(fd, VT_RELDISP, VT_ACKACQ); - close(fd); -} - -static void onReleaseDisplay() { - auto callback = daemonApp->treelandConnector()->disableRender(); - wl_callback_add_listener(callback, &renderDisabledListener, nullptr); -} - -// TreelandConnector - -TreelandConnector::TreelandConnector() : QObject(nullptr) { - setSignalHandler(); -} - -TreelandConnector::~TreelandConnector() { - delete m_notifier; - wl_display_disconnect(m_display); -} - -bool TreelandConnector::isConnected() { - return m_ddm; -} - -void TreelandConnector::setPrivateObject(struct treeland_ddm_v1 *ddm) { - m_ddm = ddm; -} - -void TreelandConnector::setSignalHandler() { - VirtualTerminal::setVtSignalHandler(onAcquireDisplay, onReleaseDisplay); -} - -// Event implementation - -static void switchToVt([[maybe_unused]] void *data, [[maybe_unused]] struct treeland_ddm_v1 *ddm, int32_t vtnr) { - int fd = open(qPrintable(VirtualTerminal::path(vtnr)), O_RDWR | O_NOCTTY); - if (ioctl(fd, VT_ACTIVATE, vtnr) < 0) - qWarning("Failed to switch to VT %d: %s", vtnr, strerror(errno)); - close(fd); -} - -static void acquireVt([[maybe_unused]] void *data, [[maybe_unused]] struct treeland_ddm_v1 *ddm, int32_t vtnr) { - int fd = open(qPrintable(VirtualTerminal::path(vtnr)), O_RDWR | O_NOCTTY); - VirtualTerminal::handleVtSwitches(fd); - close(fd); -} - -const struct treeland_ddm_v1_listener treelandDDMListener { - .switch_to_vt = switchToVt, - .acquire_vt = acquireVt, -}; - -// wayland object binding - -void registerGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - if (strcmp(interface, "treeland_ddm_v1") == 0) { + static const wl_callback_listener renderDisabledListener{ + .done = renderDisabled, + }; + + static void onAcquireDisplay() { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + + // Activate treeland session before we acknowledge VT switch. + // This will queue our rendering jobs before any keyboard event, to ensure + // all GUI elements are under a prepared state before next possible VT + // switch issued by keyboard. + int vtnr = VirtualTerminal::getVtActive(fd); + auto guard = qScopeGuard([&] { + ioctl(fd, VT_RELDISP, VT_ACKACQ); + close(fd); + }); + + auto display = displayForVt(vtnr); + if (!display) { + qWarning() << "Failed to find seat for VT" << vtnr; + return; + } + auto conn = display->connector; + auto user = daemonApp->displayManager()->findUserByVt(vtnr); + if (isVtRunningTreeland(vtnr)) { + qDebug("Activate session at VT %d for user %s", vtnr, qPrintable(user)); + conn->activateSession(); + conn->enableRender(); + conn->switchToUser(user); + } + } + + static void onReleaseDisplay() { + int vtnr = VirtualTerminal::currentVt(); + auto display = displayForVt(vtnr); + if (!display) { + qWarning() << "Failed to find seat for VT" << vtnr; + return; + } + auto callback = display->connector->disableRender(); + wl_callback_add_listener(callback, &renderDisabledListener, nullptr); + } + + ////////////////////// + // Class definition // + ////////////////////// + + TreelandConnector::TreelandConnector(Display *display) + : QObject(display) { + m_connectTimer = new QTimer(this); + m_connectTimer->setInterval(300); + QObject::connect(m_connectTimer, &QTimer::timeout, this, &TreelandConnector::tryConnect); + + VirtualTerminal::setVtSignalHandler(onAcquireDisplay, onReleaseDisplay); + } + + TreelandConnector::~TreelandConnector() { + if (m_notifier) + delete m_notifier; + if (m_display) + wl_display_disconnect(m_display); + } + + ////////////////////////// + // Event implementation // + ////////////////////////// + + static void login(void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm, + const char *username, + const char *secret, + uint32_t session_type, + const char *session_file) { + auto connector = static_cast(data); + auto display = static_cast(connector->parent()); + display->login(QString::fromLocal8Bit(username), + QString::fromLocal8Bit(secret), + Session(Session::Type(session_type), QString::fromUtf8(session_file))); + } + + static void logout(void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm, + const char *session) { + auto connector = static_cast(data); + auto display = static_cast(connector->parent()); + display->logout(QString::fromLocal8Bit(session)); + } + + static void lock(void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm, + const char *session) { + auto connector = static_cast(data); + auto display = static_cast(connector->parent()); + display->lock(QString::fromLocal8Bit(session)); + } + + static void unlock(void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm, + const char *username, + const char *secret) { auto connector = static_cast(data); - auto ddm = static_cast( - wl_registry_bind(registry, name, &treeland_ddm_v1_interface, version) - ); - treeland_ddm_v1_add_listener(ddm, &treelandDDMListener, connector); - connector->setPrivateObject(ddm); - qDebug("Connected to treeland_ddm global object"); - } -} - -void removeGlobal([[maybe_unused]] void *data, [[maybe_unused]] struct wl_registry *registry, [[maybe_unused]] uint32_t name) { - // Do not deregister the global object (set m_ddm to null) here, - // as wlroots will send global_remove event when session deactivated, - // which is not what we want. The connection will be preserved after that. -} - -const struct wl_registry_listener registryListener { - .global = registerGlobal, - .global_remove = removeGlobal, -}; - -void TreelandConnector::connect(QString socketPath) { - disconnect(); - - m_display = wl_display_connect(qPrintable(socketPath)); - auto registry = wl_display_get_registry(m_display); - - wl_registry_add_listener(registry, ®istryListener, this); - - wl_display_roundtrip(m_display); - - while (wl_display_dispatch_pending(m_display) > 0); - wl_display_flush(m_display); - m_notifier = new QSocketNotifier(wl_display_get_fd(m_display), QSocketNotifier::Read); - QObject::connect(m_notifier, &QSocketNotifier::activated, this, [this] { - if (wl_display_dispatch(m_display) == -1 || wl_display_flush(m_display) == -1) { - if (errno != EAGAIN) { + auto display = static_cast(connector->parent()); + display->unlock(QString::fromLocal8Bit(username), QString::fromLocal8Bit(secret)); + } + + static void poweroff([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm) { + daemonApp->powerManager()->powerOff(); + } + + static void reboot([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm) { + daemonApp->powerManager()->reboot(); + } + + static void suspend([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm) { + daemonApp->powerManager()->suspend(); + } + + static void hibernate([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm) { + daemonApp->powerManager()->hibernate(); + } + + static void hybridSleep([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm) { + daemonApp->powerManager()->hybridSleep(); + } + + static void switchToVt([[maybe_unused]] void *data, + [[maybe_unused]] struct treeland_ddm_v2 *ddm, + int32_t vtnr) { + int fd = open(qPrintable(VirtualTerminal::path(vtnr)), O_RDWR | O_NOCTTY); + if (ioctl(fd, VT_ACTIVATE, vtnr) < 0) + qWarning("Failed to switch to VT %d: %s", vtnr, strerror(errno)); + close(fd); + } + + const struct treeland_ddm_v2_listener treelandDDMListener{ + .login = login, + .logout = logout, + .lock = lock, + .unlock = unlock, + .poweroff = poweroff, + .reboot = reboot, + .suspend = suspend, + .hibernate = hibernate, + .hybrid_sleep = hybridSleep, + .switch_to_vt = switchToVt, + }; + + /////////////////////////////// + // Handle wayland connection // + /////////////////////////////// + + void registerGlobal(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) { + if (strcmp(interface, "treeland_ddm_v2") == 0) { + auto conn = static_cast(data); + auto proxy = static_cast( + wl_registry_bind(registry, name, &treeland_ddm_v2_interface, version)); + treeland_ddm_v2_add_listener(proxy, &treelandDDMListener, conn); + conn->proxy = proxy; + qDebug("Connected to treeland_ddm_v2 global object"); + + // Acquire VT control immediately + int fd = open(qPrintable(VirtualTerminal::path(0)), O_RDWR | O_NOCTTY); + VirtualTerminal::handleVtSwitches(fd); + close(fd); + + // Send capabilities + conn->capabilities(daemonApp->powerManager()->capabilities()); + + auto display = static_cast(conn->parent()); + for (Auth *auth : std::as_const(display->auths)) + if (auth->sessionOpened) + conn->userLoggedIn(auth->user, auth->session); + } + } + + void removeGlobal([[maybe_unused]] void *data, + [[maybe_unused]] struct wl_registry *registry, + [[maybe_unused]] uint32_t name) { + // Do not deregister the global object (set proxy to null) here, + // as wlroots will send global_remove event when session deactivated, + // which is not what we want. The connection will be preserved after that. + } + + const struct wl_registry_listener registryListener{ + .global = registerGlobal, + .global_remove = removeGlobal, + }; + + void TreelandConnector::connect() { + if (m_connectTimer->isActive()) + return; + disconnect(); + tryConnect(); + } + + void TreelandConnector::tryConnect() { + m_display = wl_display_connect("/run/treeland/wayland-0"); + if (!m_display) { + qInfo("Failed to connect to treeland, retrying..."); + if (!m_connectTimer->isActive()) + m_connectTimer->start(); + } else { + if (m_connectTimer->isActive()) + m_connectTimer->stop(); + connected(); + } + } + + void TreelandConnector::connected() { + wl_registry *registry = wl_display_get_registry(m_display); + wl_registry_add_listener(registry, ®istryListener, this); + wl_display_roundtrip(m_display); + + while (wl_display_dispatch_pending(m_display) > 0); + wl_display_flush(m_display); + m_notifier = new QSocketNotifier(wl_display_get_fd(m_display), QSocketNotifier::Read); + QObject::connect(m_notifier, &QSocketNotifier::activated, this, [&] { + if ((wl_display_dispatch(m_display) == -1 || wl_display_flush(m_display) == -1) + && errno != EAGAIN) { qWarning("Treeland connection lost!"); disconnect(); + // Auto reconnect + QTimer::singleShot(1000, this, &TreelandConnector::tryConnect); + } + }); + } + + void TreelandConnector::disconnect() { + if (m_display) { + if (m_notifier) + m_notifier->setEnabled(false); + wl_display_disconnect(m_display); + if (m_notifier) { + m_notifier->deleteLater(); + m_notifier = nullptr; } + m_display = nullptr; } - }); -} + proxy = nullptr; + qInfo("Disconnected from treeland"); + } -void TreelandConnector::disconnect() { - if (m_display) { - if (m_notifier) - m_notifier->setEnabled(false); - wl_display_disconnect(m_display); - if (m_notifier) { - m_notifier->deleteLater(); - m_notifier = nullptr; + ///////////////////// + // Request wrapper // + ///////////////////// + + void TreelandConnector::capabilities(uint32_t capabilities) const { + if (proxy) { + treeland_ddm_v2_capabilities(proxy, capabilities); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call capabilities"); } - m_display = nullptr; } - m_ddm = nullptr; -} -// Request wrapper + void TreelandConnector::userLoggedIn(const QString &username, const QString &session) const { + if (proxy) { + treeland_ddm_v2_user_logged_in(proxy, qPrintable(username), qPrintable(session)); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call userLoggedIn"); + } + } -void TreelandConnector::switchToGreeter() { - if (isConnected()) { - treeland_ddm_v1_switch_to_greeter(m_ddm); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call switchToGreeter"); + void TreelandConnector::authenticationFailed(uint32_t error) const { + if (proxy) { + treeland_ddm_v2_authentication_failed(proxy, error); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call authenticationFailed"); + } } -} -void TreelandConnector::switchToUser(const QString username) { - if (isConnected()) { - treeland_ddm_v1_switch_to_user(m_ddm, qPrintable(username)); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call switchToUser"); + void TreelandConnector::switchToGreeter() const { + if (proxy) { + treeland_ddm_v2_switch_to_greeter(proxy); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call switchToGreeter"); + } } -} -void TreelandConnector::activateSession() { - if (isConnected()) { - treeland_ddm_v1_activate_session(m_ddm); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call activateSession"); + void TreelandConnector::switchToUser(const QString &username) const { + if (proxy) { + treeland_ddm_v2_switch_to_user(proxy, qPrintable(username)); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call switchToUser"); + } } -} -void TreelandConnector::deactivateSession() { - if (isConnected()) { - treeland_ddm_v1_deactivate_session(m_ddm); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call deactivateSession"); + void TreelandConnector::activateSession() const { + if (proxy) { + treeland_ddm_v2_activate_session(proxy); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call activateSession"); + } } -} -void TreelandConnector::enableRender() { - if (isConnected()) { - treeland_ddm_v1_enable_render(m_ddm); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call enableRender"); + void TreelandConnector::deactivateSession() const { + if (proxy) { + treeland_ddm_v2_deactivate_session(proxy); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call deactivateSession"); + } } -} -struct wl_callback *TreelandConnector::disableRender() { - if (isConnected()) { - auto callback = treeland_ddm_v1_disable_render(m_ddm); - wl_display_flush(m_display); - return callback; - } else { - qWarning("Treeland is not connected when trying to call disableRender"); - return nullptr; + void TreelandConnector::enableRender() const { + if (proxy) { + treeland_ddm_v2_enable_render(proxy); + wl_display_flush(m_display); + } else { + qWarning("Treeland is not connected when trying to call enableRender"); + } + } + + struct wl_callback *TreelandConnector::disableRender() const { + if (proxy) { + auto callback = treeland_ddm_v2_disable_render(proxy); + wl_display_flush(m_display); + return callback; + } else { + qWarning("Treeland is not connected when trying to call disableRender"); + return nullptr; + } } -} -} +} // namespace DDM diff --git a/src/daemon/TreelandConnector.h b/src/daemon/TreelandConnector.h index 9933cb9..cedee7d 100644 --- a/src/daemon/TreelandConnector.h +++ b/src/daemon/TreelandConnector.h @@ -2,34 +2,65 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include + +class QSocketNotifier; +class QTimer; struct wl_display; struct wl_callback; -struct treeland_ddm_v1; +struct treeland_ddm_v2; namespace DDM { -class TreelandConnector : QObject { - Q_OBJECT -public: - TreelandConnector(); - ~TreelandConnector(); - bool isConnected(); - void setPrivateObject(struct treeland_ddm_v1 *ddm); - void setSignalHandler(); - void connect(const QString socketPath); - void disconnect(); - - void switchToGreeter(); - void switchToUser(const QString username); - void ackVtSwitch(const int vtnr); - void activateSession(); - void deactivateSession(); - void enableRender(); - struct wl_callback *disableRender(); -private: - struct wl_display *m_display { nullptr }; - QSocketNotifier *m_notifier { nullptr }; - struct treeland_ddm_v1 *m_ddm { nullptr }; -}; -} + class Display; + + class TreelandConnector : public QObject { + Q_OBJECT + public: + TreelandConnector(Display *display); + ~TreelandConnector(); + + /** + * @brief Connect to treeland. + * + * Continuously tries until success. + * + * This should be called by Display when starting. + */ + void connect(); + + /** + * @brief Disconnect from treeland. + * + * This should be called by Display when stopping. + */ + void disconnect(); + + /////////////////////////////////////////////////////////// + // Request wrappers // + // Call them once you want to send something to treeland // + // Documentations are available in treeland-ddm-v2.xml // + /////////////////////////////////////////////////////////// + + void capabilities(uint32_t capabilities) const; + void userLoggedIn(const QString &username, const QString &session) const; + void authenticationFailed(uint32_t error) const; + void switchToGreeter() const; + void switchToUser(const QString &username) const; + void activateSession() const; + void deactivateSession() const; + void enableRender() const; + struct wl_callback *disableRender() const; + + /** The proxy object for sending requests. Mainly for private use. */ + struct treeland_ddm_v2 *proxy{ nullptr }; + + private Q_SLOTS: + void tryConnect(); + void connected(); + + private: + struct wl_display *m_display{ nullptr }; + QSocketNotifier *m_notifier{ nullptr }; + QTimer *m_connectTimer{ nullptr }; + }; +} // namespace DDM diff --git a/src/daemon/TreelandDisplayServer.cpp b/src/daemon/TreelandDisplayServer.cpp index 06bf3ca..8a87f42 100644 --- a/src/daemon/TreelandDisplayServer.cpp +++ b/src/daemon/TreelandDisplayServer.cpp @@ -2,36 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "TreelandDisplayServer.h" -#include "Messages.h" -#include "SocketServer.h" -#include "SocketWriter.h" #include "Display.h" #include #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include using namespace DDM; -TreelandDisplayServer::TreelandDisplayServer(SocketServer *socketServer, Display *parent) - : QObject(parent) - , m_socketServer(socketServer) { - connect(m_socketServer, &SocketServer::connected, this, [this, parent](QLocalSocket *socket) { - m_greeterSockets << socket; - }); - connect(m_socketServer, &SocketServer::disconnected, this, [this](QLocalSocket *socket) { - m_greeterSockets.removeOne(socket); - }); -} +TreelandDisplayServer::TreelandDisplayServer(Display *parent) + : QObject(parent) { } TreelandDisplayServer::~TreelandDisplayServer() { stop(); @@ -66,19 +45,3 @@ void TreelandDisplayServer::stop() { // Reset flag m_started = false; } - -void TreelandDisplayServer::activateUser(const QString &user, int xdgSessionId) { - for (auto greeter : m_greeterSockets) { - if (user == "dde") { - SocketWriter(greeter) << quint32(DaemonMessages::SwitchToGreeter); - } - - SocketWriter(greeter) << quint32(DaemonMessages::UserActivateMessage) << user << xdgSessionId; - } -} - -void TreelandDisplayServer::onLoginFailed(const QString &user) { - for (auto greeter : m_greeterSockets) { - SocketWriter(greeter) << quint32(DaemonMessages::LoginFailed) << user; - } -} diff --git a/src/daemon/TreelandDisplayServer.h b/src/daemon/TreelandDisplayServer.h index c548ea4..f9664fd 100644 --- a/src/daemon/TreelandDisplayServer.h +++ b/src/daemon/TreelandDisplayServer.h @@ -1,32 +1,23 @@ // Copyright (C) 2023 Dingyuan Zhang . // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - -class QLocalSocket; -class QLocalServer; +#include namespace DDM { class Display; - class SocketServer; class TreelandDisplayServer : public QObject { Q_OBJECT Q_DISABLE_COPY(TreelandDisplayServer) public: - explicit TreelandDisplayServer(DDM::SocketServer *socketServer, Display *parent); + explicit TreelandDisplayServer(Display *parent); ~TreelandDisplayServer(); public Q_SLOTS: bool start(); void stop(); - void activateUser(const QString &user, int xdgSessionId); - void onLoginFailed(const QString &user); private: - SocketServer *m_socketServer; - QList m_greeterSockets; bool m_started{ false }; }; }