From 723840133de0b119a343b8596c63e4829f6a6376 Mon Sep 17 00:00:00 2001 From: April & May Date: Wed, 3 Sep 2025 13:31:24 +0800 Subject: [PATCH] feat: Starting sessions other than treeland using ddm This commit enables starting sessions other than treeland with ddm. It use treeland as greeter to handle user login. Once user logged in, it kills the wayland compositor and start the desired user session. --- data/scripts/Xsetup | 3 ++ src/auth/Auth.cpp | 39 +++++++++++++++++++++++++- src/auth/Auth.h | 12 +++++++- src/daemon/Display.cpp | 56 +++++++++++++++++++++++++++++++++----- src/daemon/Greeter.cpp | 19 +++++++++++-- src/daemon/Greeter.h | 5 ++++ src/helper/HelperApp.cpp | 6 +++- src/helper/HelperApp.h | 1 + src/helper/UserSession.cpp | 13 +++------ 9 files changed, 133 insertions(+), 21 deletions(-) diff --git a/data/scripts/Xsetup b/data/scripts/Xsetup index 8c7c2aa..4c29d0e 100755 --- a/data/scripts/Xsetup +++ b/data/scripts/Xsetup @@ -1,3 +1,6 @@ #!/bin/sh # Xsetup - run as root before the login dialog appears +# Allow all client connect to the Xorg server. Since our X server is started as +# root, but sessions are started as user. +xhost + diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp index ce1fe81..1eed7f7 100644 --- a/src/auth/Auth.cpp +++ b/src/auth/Auth.cpp @@ -65,6 +65,8 @@ namespace DDM { QLocalSocket *socket { nullptr }; QString displayServerCmd; QString sessionPath { }; + Session::Type sessionType { Session::UnknownSession }; + QString sessionFileName { }; QString user { }; QString password { }; QByteArray cookie { }; @@ -72,6 +74,7 @@ namespace DDM { bool greeter { false }; bool singleMode { false }; bool identifyOnly { false }; + bool skipAuth { false }; QProcessEnvironment environment { }; qint64 id { 0 }; static qint64 lastId; @@ -317,6 +320,18 @@ namespace DDM { return d->sessionId; } + Session::Type Auth::sessionType() const { + return d->sessionType; + } + + QString Auth::sessionFileName() const { + return d->sessionFileName; + } + + bool Auth::isSingleMode() const { + return d->singleMode; + } + bool Auth::isActive() const { return d->child->state() != QProcess::NotRunning; } @@ -394,6 +409,14 @@ namespace DDM { } } + void Auth::setSessionType(const Session::Type type) { + d->sessionType = type; + } + + void Auth::setSessionFileName(const QString &fileName) { + d->sessionFileName = fileName; + } + int Auth::tty() const { return d->tty; } @@ -424,6 +447,12 @@ namespace DDM { } } + void Auth::setSkipAuth(bool on) { + if (on != d->skipAuth) { + d->skipAuth = on; + } + } + void Auth::start() { QStringList args; args << QStringLiteral("--socket") << SocketServer::instance()->fullServerName(); @@ -442,6 +471,8 @@ namespace DDM { args << QStringLiteral("--single-mode"); if (d->identifyOnly) args << QStringLiteral("--identify-only"); + if (d->skipAuth) + args << QStringLiteral("--skip-auth"); d->child->start(QStringLiteral("%1/ddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); } @@ -453,7 +484,13 @@ namespace DDM { d->child->terminate(); // wait for finished - if (!d->child->waitForFinished(5000)) + // TODO: Cut off the waiting. + // The code will be executed when user trying to start sessions other + // than treeland, which will stop the currentAuth and start a new one. + // This process involves the removal of seatd client, which needs a + // small amount of time to wait. Consider to make this process under + // control. + if (!d->child->waitForFinished(500)) d->child->kill(); } } diff --git a/src/auth/Auth.h b/src/auth/Auth.h index bb8c6d8..5f66480 100644 --- a/src/auth/Auth.h +++ b/src/auth/Auth.h @@ -23,6 +23,7 @@ #include "AuthRequest.h" #include "AuthPrompt.h" +#include "Session.h" #include #include @@ -97,12 +98,15 @@ namespace DDM { bool isGreeter() const; bool verbose() const; bool identifyOnly() const; + bool isSingleMode() const; const QByteArray &cookie() const; const QString &user() const; const QString &session() const; const QString &password() const; AuthRequest *request(); QString sessionId() const; + Session::Type sessionType() const; + QString sessionFileName() const; int tty() const; void setTTY(int tty); @@ -146,6 +150,8 @@ namespace DDM { void setVerbose(bool on = true); void setIdentifyOnly(bool on = false); + + void setSkipAuth(bool on = true); /** * Sets the user which will then authenticate * @param user username @@ -179,7 +185,11 @@ namespace DDM { */ void setSingleMode(bool on = true); - void setSessionId(const QString& sessionId); + void setSessionId(const QString &sessionId); + + void setSessionType(const Session::Type type); + + void setSessionFileName(const QString &fileName); public Q_SLOTS: /** diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index fcef17d..2819d74 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -590,11 +590,13 @@ namespace DDM { } auth->setUser(user); + auth->setSessionType(session.type()); + auth->setSessionFileName(session.fileName()); if (m_reuseSessionId.isNull()) { auth->setSession(session.exec()); } auth->insertEnvironment(env); - auth->setSingleMode(m_displayServerType == DisplayServerType::SingleCompositerServerType); + auth->setSingleMode(session.isSingleMode()); auth->start(); } @@ -634,22 +636,61 @@ namespace DDM { else stateConfig.Last.User.setDefault(); if (mainConfig.Users.RememberLastSession.get()) - stateConfig.Last.Session.set(auth->session()); + stateConfig.Last.Session.set(auth->sessionFileName()); else stateConfig.Last.Session.setDefault(); stateConfig.save(); - if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { + if (auth->isSingleMode()) { auto* server = reinterpret_cast(m_displayServer); server->onLoginSucceeded(user); + switchToUser(auth->user()); } else { if (m_socket) { emit loginSucceeded(m_socket, user); daemonApp->displayManager()->setLastSession(auth->sessionId()); } - } + if (auth->identifyOnly()) + return; - switchToUser(auth->user()); + // Stop the original suit of displayServer since it has finished its job. + disconnect(m_displayServer, &DisplayServer::stopped, this, &Display::stop); + m_displayServer->stop(); + delete m_displayServer; + m_greeter->stop(); + delete m_greeter; + m_currentAuth->stop(); + m_socketServer->stop(); + delete m_socketServer; + + // Start the target displayServer + m_terminalId = m_sessionTerminalId = fetchAvailableVt(); + m_started = false; + + if (auth->sessionType() == Session::X11Session) { + m_displayServer = new XorgDisplayServer(this); + m_displayServerType = X11DisplayServerType; + } else { + m_displayServer = new WaylandDisplayServer(this); + m_displayServerType = WaylandDisplayServerType; + } + connect(m_displayServer, &DisplayServer::started, this, &Display::displayServerStarted); + connect(m_displayServer, &DisplayServer::stopped, this, &Display::stop); + + m_socketServer = new SocketServer(this); + + // The greeter here is used to start user session. + m_greeter = new Greeter(this); + m_greeter->setDisplayServerCommand(auth->session()); + m_greeter->setUser(user); + m_greeter->setSkipAuth(); + + connect(m_greeter, &Greeter::failed, this, &Display::stop); + connect(m_greeter, &Greeter::displayServerFailed, this, &Display::displayServerFailed); + connect(m_greeter, &Greeter::succeed, this, &Display::stop); + + m_displayServer->start(); + } } m_socket = nullptr; } @@ -711,7 +752,8 @@ namespace DDM { } } - if (status != Auth::HELPER_AUTH_ERROR && m_displayServerType != DisplayServerType::SingleCompositerServerType) + // Don't restart display when ddm-helper crashed (exit code 9) + if (status != Auth::HELPER_AUTH_ERROR && status != Auth::HelperExitStatus(9) && m_displayServerType != DisplayServerType::SingleCompositerServerType) stop(); } @@ -730,7 +772,7 @@ namespace DDM { void Display::slotSessionStarted(bool success) { qDebug() << "Session started" << success; Auth* auth = qobject_cast(sender()); - if (m_displayServerType == SingleCompositerServerType) { + if (auth->isSingleMode()) { switchToUser(auth->user()); } diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp index 916fa36..d56dcdb 100644 --- a/src/daemon/Greeter.cpp +++ b/src/daemon/Greeter.cpp @@ -83,6 +83,14 @@ namespace DDM { m_singleMode = on; } + void Greeter::setUser(const QString &user) { + m_user = user; + } + + void Greeter::setSkipAuth(bool on) { + m_skipAuth = on; + } + void Greeter::setUserActivated(bool active) { m_userActivated = active; } @@ -226,7 +234,8 @@ namespace DDM { if (m_display->displayServerType() == Display::X11DisplayServerType) { env.insert(QStringLiteral("DISPLAY"), m_display->name()); env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); - m_auth->setCookie(qobject_cast(displayServer)->cookie()); + if (m_display->sessionType() == "x11") + m_auth->setCookie(qobject_cast(displayServer)->cookie()); } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); @@ -240,7 +249,7 @@ namespace DDM { qDebug() << "Greeter starting..."; // start greeter - m_auth->setUser(QStringLiteral("dde")); + m_auth->setUser(m_user); QString displayServerCmd = m_displayServerCmd; if (m_singleMode) { displayServerCmd += " --lockscreen"; @@ -255,6 +264,10 @@ namespace DDM { return true; } + if (m_skipAuth) { + m_auth->setSkipAuth(); + } + m_auth->start(); m_tryTimer->start(); @@ -371,6 +384,8 @@ namespace DDM { Q_EMIT ttyFailed(); } else if (status == Auth::HELPER_SESSION_ERROR) { Q_EMIT failed(); + } else if (status == Auth::HELPER_SUCCESS) { + Q_EMIT succeed(); } } diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h index ed4e3a6..235d3a8 100644 --- a/src/daemon/Greeter.h +++ b/src/daemon/Greeter.h @@ -42,6 +42,8 @@ namespace DDM { void setSocket(const QString &socket); void setTheme(const QString &theme); void setSingleMode(bool on = true); + void setUser(const QString &user); + void setSkipAuth(bool on = true); void setUserActivated(bool active); QString displayServerCommand() const; @@ -67,6 +69,7 @@ namespace DDM { void ttyFailed(); void failed(); void displayServerFailed(); + void succeed(); void greeterStarted(); private: @@ -75,6 +78,8 @@ namespace DDM { bool m_userActivated { false }; int m_currentRetry { 0 }; int m_maxRetry{ 3 }; + QString m_user = QStringLiteral("dde"); + bool m_skipAuth { false }; Display * const m_display { nullptr }; QString m_socket; diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp index 380bbde..bfc233a 100644 --- a/src/helper/HelperApp.cpp +++ b/src/helper/HelperApp.cpp @@ -123,6 +123,10 @@ namespace DDM { m_backend->setIdentifyOnly(true); } + if ((pos = args.indexOf(QStringLiteral("--skip-auth"))) >= 0) { + m_skipAuth = true; + } + if (server.isEmpty() || m_id <= 0) { qCritical() << "This application is not supposed to be executed manually"; exit(Auth::HELPER_OTHER_ERROR); @@ -163,7 +167,7 @@ namespace DDM { } Q_ASSERT(getuid() == 0); - if (!m_backend->authenticate()) { + if (!m_skipAuth && !m_backend->authenticate()) { authenticated(QString()); // write failed login to btmp diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h index 5b948a9..8db81a5 100644 --- a/src/helper/HelperApp.h +++ b/src/helper/HelperApp.h @@ -67,6 +67,7 @@ namespace DDM { QString m_user { }; // TODO: get rid of this in a nice clean way along the way with moving to user session X server QByteArray m_cookie { }; + bool m_skipAuth = false; /*! \brief Write utmp/wtmp/btmp records when a user logs in diff --git a/src/helper/UserSession.cpp b/src/helper/UserSession.cpp index 03c673c..a2c005d 100644 --- a/src/helper/UserSession.cpp +++ b/src/helper/UserSession.cpp @@ -122,14 +122,9 @@ namespace DDM { } qInfo() << "Starting X11 session:" << m_displayServerCmd << command; - if (m_displayServerCmd.isEmpty()) { - auto args = QProcess::splitCommand(command); - setProgram(args.takeFirst()); - setArguments(args); - } else { - setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/ddm-helper-start-x11user")); - setArguments({m_displayServerCmd, command}); - } + auto args = QProcess::splitCommand(m_displayServerCmd); + setProgram(args.takeFirst()); + setArguments(args); QProcess::start(); } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { @@ -238,7 +233,7 @@ namespace DDM { // take control of the tty if (takeControl) { - if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0) { + if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) { const auto error = strerror(errno); qCritical().nospace() << "Failed to take control of " << ttyString << " (" << QFileInfo(ttyString).owner() << "): " << error; _exit(Auth::HELPER_TTY_ERROR);