From 3f55fc9ca0f289a9b88bff65480fd720f6020e11 Mon Sep 17 00:00:00 2001 From: April & May & June Date: Wed, 25 Feb 2026 19:18:43 +0800 Subject: [PATCH 1/3] feat: Upgrade treeland_ddm protocol to version 2 In this upgrade we rewrite all treeland-ddm interactions, includes those originally in SocketServer / GreeterProxy, into wayland communication. There's only one true treeland-ddm interaction which should on behalf of the Wayland protocol. --- src/daemon/CMakeLists.txt | 16 +- src/daemon/TreelandConnector.cpp | 678 ++++++++++++++++++++----------- src/daemon/TreelandConnector.h | 83 ++-- 3 files changed, 500 insertions(+), 277 deletions(-) diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 5347f96..8a46282 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) 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 From d71ea937c0faf47cba526fefbb1b883e9be0b7d8 Mon Sep 17 00:00:00 2001 From: April & May & June Date: Wed, 25 Feb 2026 19:22:52 +0800 Subject: [PATCH 2/3] feat: Remove SocketServer, unify treeland-ddm communication with wayland With the upgrade we did in previous commit, SocketServer is not needed now, and we shall reform our communication with our greeter the Treeland, to use the newly formed Wayland protocol. --- REUSE.toml | 2 +- src/daemon/CMakeLists.txt | 1 - src/daemon/DaemonApp.cpp | 1 - src/daemon/DaemonApp.h | 3 - src/daemon/Display.cpp | 119 ++++--------- src/daemon/Display.h | 41 ++--- src/daemon/SeatManager.cpp | 3 +- src/daemon/SocketServer.cpp | 247 --------------------------- src/daemon/SocketServer.h | 70 -------- src/daemon/TreelandDisplayServer.cpp | 41 +---- src/daemon/TreelandDisplayServer.h | 13 +- 11 files changed, 52 insertions(+), 489 deletions(-) delete mode 100644 src/daemon/SocketServer.cpp delete mode 100644 src/daemon/SocketServer.h 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/CMakeLists.txt b/src/daemon/CMakeLists.txt index 8a46282..4734023 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -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/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 }; }; } From 2938a5af300f64827a733b9f878c2e12e26665be Mon Sep 17 00:00:00 2001 From: April & May & June Date: Wed, 25 Feb 2026 19:24:51 +0800 Subject: [PATCH 3/3] feat: Use string for logind session id instead of int Although the logind session id is exactly an integer in current systemd-logind, but this is an UB according to systemd documentation. Systemd advertise us to treat it as a string and we shall adapt it. --- src/daemon/Auth.cpp | 50 +++++++++++++++++++++++---------------------- src/daemon/Auth.h | 12 +++++------ 2 files changed, 32 insertions(+), 30 deletions(-) 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