diff options
Diffstat (limited to 'user/sddm/rootless-xorg.patch')
-rw-r--r-- | user/sddm/rootless-xorg.patch | 2012 |
1 files changed, 0 insertions, 2012 deletions
diff --git a/user/sddm/rootless-xorg.patch b/user/sddm/rootless-xorg.patch deleted file mode 100644 index 99a2e189d..000000000 --- a/user/sddm/rootless-xorg.patch +++ /dev/null @@ -1,2012 +0,0 @@ -From 9881225a92ffe6cf3d4a84273192b08b2d2f6046 Mon Sep 17 00:00:00 2001 -From: Pier Luigi Fiorini <pierluigi.fiorini@liri.io> -Date: Tue, 27 Aug 2019 22:31:00 +0200 -Subject: [PATCH] X11 display server without root privileges - -Introduce an alternative display server option to run the greeter with -the X11 server as an unprivileged user. - -Root privileges are required by default, but this will change in the -future. - -Greeter output is forwarded to the helper process, instead of saving it -into a separate file like user sessions do. -This means the greeter output is available in the journal with the -daemon and helper logs. - -Display start and stop commands are executed as sddm user and this might -break the workflow of our users. -That is the reason why we still run Xorg as root for the greeter by -default. - -X11 user session always spawn a new display server without root -privileges. - -Closes: #246 ---- - CMakeLists.txt | 1 + - data/man/sddm.conf.rst.in | 14 +- - src/auth/Auth.cpp | 24 ++- - src/auth/Auth.h | 14 ++ - src/auth/AuthMessages.h | 1 + - src/common/Configuration.h | 3 + - src/common/Session.cpp | 10 ++ - src/common/Session.h | 4 + - src/common/XAuth.cpp | 127 ++++++++++++++ - src/common/XAuth.h | 55 ++++++ - src/daemon/CMakeLists.txt | 6 +- - src/daemon/Display.cpp | 64 +++++-- - src/daemon/Display.h | 11 ++ - src/daemon/Greeter.cpp | 41 +++-- - src/daemon/Greeter.h | 5 + - src/daemon/XorgDisplayServer.cpp | 78 ++------- - src/daemon/XorgDisplayServer.h | 10 +- - src/daemon/XorgUserDisplayServer.cpp | 102 +++++++++++ - src/daemon/XorgUserDisplayServer.h | 53 ++++++ - src/greeter/GreeterApp.cpp | 9 + - src/helper/Backend.cpp | 5 + - src/helper/Backend.h | 2 + - src/helper/CMakeLists.txt | 4 + - src/helper/HelperApp.cpp | 30 +++- - src/helper/HelperApp.h | 1 + - src/helper/UserSession.cpp | 202 +++++++++++++-------- - src/helper/UserSession.h | 20 ++- - src/helper/backend/PamBackend.cpp | 6 +- - src/helper/xorguserhelper.cpp | 252 +++++++++++++++++++++++++++ - src/helper/xorguserhelper.h | 64 +++++++ - 30 files changed, 1036 insertions(+), 182 deletions(-) - create mode 100644 src/common/XAuth.cpp - create mode 100644 src/common/XAuth.h - create mode 100644 src/daemon/XorgUserDisplayServer.cpp - create mode 100644 src/daemon/XorgUserDisplayServer.h - create mode 100644 src/helper/xorguserhelper.cpp - create mode 100644 src/helper/xorguserhelper.h - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 9614b4e1e..41aee21d4 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -16,6 +16,7 @@ set(SDDM_VERSION_MAJOR 0) - set(SDDM_VERSION_MINOR 19) - set(SDDM_VERSION_PATCH 0) - set(SDDM_VERSION_STRING "${SDDM_VERSION_MAJOR}.${SDDM_VERSION_MINOR}.${SDDM_VERSION_PATCH}") -+add_compile_definitions("SDDM_VERSION=\"${SDDM_VERSION_STRING}\"") - - # Set up packaging - set(CPACK_PACKAGE_NAME "sddm") -diff --git a/data/man/sddm.conf.rst.in b/data/man/sddm.conf.rst.in -index 1061540c0..960e65c68 100644 ---- a/data/man/sddm.conf.rst.in -+++ b/data/man/sddm.conf.rst.in -@@ -6,7 +6,7 @@ - sddm display manager configuration - ---------------------------------- - --:Date: August 2015 -+:Date: March 2021 - :Version: sddm @SDDM_VERSION_STRING@ - :Manual section: 5 - :Manual group: sddm -@@ -36,6 +36,14 @@ OPTIONS - - [General] section: - -+`DisplayServer=` -+ Select the display server to use for the greeter. -+ Valid values are: -+ * `x11`: X server running as root. -+ * `x11-user`: X server running as unprivileged user. -+ Default value is "x11". -+ For `x11-user` you might need to configure Xorg.wrap(1). -+ - `HaltCommand=` - Halt command. - Default value is "@HALT_COMMAND@". -@@ -134,10 +142,14 @@ OPTIONS - - `DisplayCommand=` - Path of script to execute when starting the display server. -+ The script will be executed as root when General.DisplayServer -+ is "x11", otherwise as sddm user. - Default value is "@DATA_INSTALL_DIR@/scripts/Xsetup". - - `DisplayStopCommand=` - Path of script to execute when stopping the display server. -+ The script will be executed as root when General.DisplayServer -+ is "x11", otherwise as sddm user. - Default value is "@DATA_INSTALL_DIR@/scripts/Xstop". - - `MinimumVT=` -diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp -index 403186ee1..4450cc606 100644 ---- a/src/auth/Auth.cpp -+++ b/src/auth/Auth.cpp -@@ -62,6 +62,7 @@ namespace SDDM { - AuthRequest *request { nullptr }; - QProcess *child { nullptr }; - QLocalSocket *socket { nullptr }; -+ QString displayServerCmd; - QString sessionPath { }; - QString user { }; - QString cookie { }; -@@ -202,6 +203,15 @@ namespace SDDM { - str.send(); - break; - } -+ case DISPLAY_SERVER_STARTED: { -+ QString displayName; -+ str >> displayName; -+ Q_EMIT auth->displayServerReady(displayName); -+ str.reset(); -+ str << DISPLAY_SERVER_STARTED; -+ str.send(); -+ break; -+ } - default: { - Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); - } -@@ -210,7 +220,9 @@ namespace SDDM { - - void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) { - if (exitStatus != QProcess::NormalExit) { -- qWarning("Auth: sddm-helper crashed (exit code %d)", exitCode); -+ qWarning("Auth: sddm-helper (%s) crashed (exit code %d)", -+ qPrintable(child->arguments().join(QLatin1Char(' '))), -+ HelperExitStatus(exitStatus)); - Q_EMIT qobject_cast<Auth*>(parent())->error(child->errorString(), ERROR_INTERNAL); - } - -@@ -334,6 +346,14 @@ namespace SDDM { - } - } - -+ void Auth::setDisplayServerCommand(const QString &command) -+ { -+ if (d->displayServerCmd != command) { -+ d->displayServerCmd = command; -+ Q_EMIT displayServerCommandChanged(); -+ } -+ } -+ - void Auth::setSession(const QString& path) { - if (path != d->sessionPath) { - d->sessionPath = path; -@@ -361,6 +381,8 @@ namespace SDDM { - args << QStringLiteral("--user") << d->user; - if (d->autologin) - args << QStringLiteral("--autologin"); -+ if (!d->displayServerCmd.isEmpty()) -+ args << QStringLiteral("--display-server") << d->displayServerCmd; - if (d->greeter) - args << QStringLiteral("--greeter"); - d->child->start(QStringLiteral("%1/sddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); -diff --git a/src/auth/Auth.h b/src/auth/Auth.h -index f7cb8acd3..dc3df24d1 100644 ---- a/src/auth/Auth.h -+++ b/src/auth/Auth.h -@@ -141,6 +141,12 @@ namespace SDDM { - */ - void setUser(const QString &user); - -+ /** -+ * Set the display server command to be started before the greeter. -+ * @param command Command of the display server to be started -+ */ -+ void setDisplayServerCommand(const QString &command); -+ - /** - * Set the session to be started after authenticating. - * @param path Path of the session executable to be started -@@ -165,6 +171,7 @@ namespace SDDM { - void verboseChanged(); - void cookieChanged(); - void userChanged(); -+ void displayServerCommandChanged(); - void sessionChanged(); - void requestChanged(); - -@@ -186,6 +193,13 @@ namespace SDDM { - */ - void sessionStarted(bool success, qint64 pid); - -+ /** -+ * Emitted when the display server is ready. -+ * -+ * @param displayName display name -+ */ -+ void displayServerReady(const QString &displayName); -+ - /** - * Emitted when the helper quits, either after authentication or when the session ends. - * Or, when something goes wrong. -diff --git a/src/auth/AuthMessages.h b/src/auth/AuthMessages.h -index 3bc97b6ba..6aea7483f 100644 ---- a/src/auth/AuthMessages.h -+++ b/src/auth/AuthMessages.h -@@ -97,6 +97,7 @@ namespace SDDM { - REQUEST, - AUTHENTICATED, - SESSION_STATUS, -+ DISPLAY_SERVER_STARTED, - MSG_LAST, - }; - -diff --git a/src/common/Configuration.h b/src/common/Configuration.h -index b79871988..47bfa2710 100644 ---- a/src/common/Configuration.h -+++ b/src/common/Configuration.h -@@ -37,6 +37,9 @@ namespace SDDM { - enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF }; - - // Name Type Default value Description -+ // TODO: Change default to x11-user in a future release -+ Entry(DisplayServer, QString, _S("x11"), _S("Which display server should be used.\n" -+ "Valid values are: x11, x11-user.")); - Entry(HaltCommand, QString, _S(HALT_COMMAND), _S("Halt command")); - Entry(RebootCommand, QString, _S(REBOOT_COMMAND), _S("Reboot command")); - Entry(Numlock, NumState, NUM_NONE, _S("Initial NumLock state. Can be on, off or none.\n" -diff --git a/src/common/Session.cpp b/src/common/Session.cpp -index a026c1f87..1b932c57a 100644 ---- a/src/common/Session.cpp -+++ b/src/common/Session.cpp -@@ -52,6 +52,16 @@ namespace SDDM { - return m_type; - } - -+ int Session::vt() const -+ { -+ return m_vt; -+ } -+ -+ void Session::setVt(int vt) -+ { -+ m_vt = vt; -+ } -+ - QString Session::xdgSessionType() const - { - return m_xdgSessionType; -diff --git a/src/common/Session.h b/src/common/Session.h -index aa196e9c6..3abc993fb 100644 ---- a/src/common/Session.h -+++ b/src/common/Session.h -@@ -43,6 +43,9 @@ namespace SDDM { - - Type type() const; - -+ int vt() const; -+ void setVt(int vt); -+ - QString xdgSessionType() const; - - QDir directory() const; -@@ -70,6 +73,7 @@ namespace SDDM { - QProcessEnvironment parseEnv(const QString &list); - bool m_valid; - Type m_type; -+ int m_vt = 0; - QDir m_dir; - QString m_name; - QString m_fileName; -diff --git a/src/common/XAuth.cpp b/src/common/XAuth.cpp -new file mode 100644 -index 000000000..bc7b10caf ---- /dev/null -+++ b/src/common/XAuth.cpp -@@ -0,0 +1,127 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* Copyright (c) 2013 Abdurrahman AVCI <abdurrahmanavci@gmail.com> -+* -+* 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 <QDebug> -+#include <QDir> -+#include <QUuid> -+ -+#include "Configuration.h" -+#include "Constants.h" -+#include "XAuth.h" -+ -+#include <random> -+ -+namespace SDDM { -+ -+XAuth::XAuth() -+{ -+ m_authDir = QStringLiteral(RUNTIME_DIR); -+} -+ -+QString XAuth::authDirectory() const -+{ -+ return m_authDir; -+} -+ -+void XAuth::setAuthDirectory(const QString &path) -+{ -+ if (m_setup) { -+ qWarning("Unable to set xauth directory after setup"); -+ return; -+ } -+ -+ m_authDir = path; -+} -+ -+QString XAuth::authPath() const -+{ -+ return m_authPath; -+} -+ -+QString XAuth::cookie() const -+{ -+ return m_cookie; -+} -+ -+void XAuth::setup() -+{ -+ if (m_setup) -+ return; -+ -+ m_setup = true; -+ -+ // Create directory if not existing -+ QDir().mkpath(m_authDir); -+ -+ // Set path -+ m_authPath = QStringLiteral("%1/%2").arg(m_authDir).arg(QUuid::createUuid().toString(QUuid::WithoutBraces)); -+ qDebug() << "Xauthority path:" << m_authPath; -+ -+ // Generate cookie -+ std::random_device rd; -+ std::mt19937 gen(rd()); -+ std::uniform_int_distribution<> dis(0, 15); -+ -+ // Reseve 32 bytes -+ m_cookie.reserve(32); -+ -+ // Create a random hexadecimal number -+ const char *digits = "0123456789abcdef"; -+ for (int i = 0; i < 32; ++i) -+ m_cookie[i] = QLatin1Char(digits[dis(gen)]); -+} -+ -+bool XAuth::addCookie(const QString &display) -+{ -+ if (!m_setup) { -+ qWarning("Please setup xauth before adding a cookie"); -+ return false; -+ } -+ -+ return XAuth::addCookieToFile(display, m_authPath, m_cookie); -+} -+ -+bool XAuth::addCookieToFile(const QString &display, const QString &fileName, -+ const QString &cookie) -+{ -+ qDebug() << "Adding cookie to" << fileName; -+ -+ // Touch file -+ QFile file_handler(fileName); -+ file_handler.open(QIODevice::Append); -+ file_handler.close(); -+ -+ QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(fileName); -+ -+ // Execute xauth -+ FILE *fp = ::popen(qPrintable(cmd), "w"); -+ -+ // Check file -+ if (!fp) -+ return false; -+ fprintf(fp, "remove %s\n", qPrintable(display)); -+ fprintf(fp, "add %s . %s\n", qPrintable(display), qPrintable(cookie)); -+ fprintf(fp, "exit\n"); -+ -+ // Close pipe -+ return pclose(fp) == 0; -+} -+ -+} // namespace SDDM -diff --git a/src/common/XAuth.h b/src/common/XAuth.h -new file mode 100644 -index 000000000..3e80f4ead ---- /dev/null -+++ b/src/common/XAuth.h -@@ -0,0 +1,55 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* Copyright (c) 2013 Abdurrahman AVCI <abdurrahmanavci@gmail.com> -+* -+* 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 SDDM_XAUTH_H -+#define SDDM_XAUTH_H -+ -+#include <QString> -+ -+namespace SDDM { -+ -+class XAuth -+{ -+public: -+ XAuth(); -+ -+ QString authDirectory() const; -+ void setAuthDirectory(const QString &path); -+ -+ QString authPath() const; -+ QString cookie() const; -+ -+ void setup(); -+ bool addCookie(const QString &display); -+ -+ static bool addCookieToFile(const QString &display, -+ const QString &fileName, -+ const QString &cookie); -+ -+private: -+ bool m_setup = false; -+ QString m_authDir; -+ QString m_authPath; -+ QString m_cookie; -+}; -+ -+} // namespace SDDM -+ -+#endif // SDDM_XAUTH_H -diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt -index 86d014bec..b411e42bd 100644 ---- a/src/daemon/CMakeLists.txt -+++ b/src/daemon/CMakeLists.txt -@@ -13,6 +13,8 @@ set(DAEMON_SOURCES - ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp - ${CMAKE_SOURCE_DIR}/src/common/Session.cpp - ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp -+ ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp -+ ${CMAKE_SOURCE_DIR}/src/common/XAuth.h - ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp -@@ -22,13 +24,15 @@ set(DAEMON_SOURCES - DisplayManager.cpp - DisplayServer.cpp - LogindDBusTypes.cpp -- XorgDisplayServer.cpp - Greeter.cpp - PowerManager.cpp - Seat.cpp - SeatManager.cpp - SignalHandler.cpp - SocketServer.cpp -+ XorgDisplayServer.cpp -+ XorgUserDisplayServer.cpp -+ XorgUserDisplayServer.h - ) - - # Different implementations of the VT switching code -diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp -index 3c77454fc..26ca3bb60 100644 ---- a/src/daemon/Display.cpp -+++ b/src/daemon/Display.cpp -@@ -25,6 +25,7 @@ - #include "DaemonApp.h" - #include "DisplayManager.h" - #include "XorgDisplayServer.h" -+#include "XorgUserDisplayServer.h" - #include "Seat.h" - #include "SocketServer.h" - #include "Greeter.h" -@@ -56,7 +57,6 @@ - namespace SDDM { - Display::Display(Seat *parent) : QObject(parent), - m_auth(new Auth(this)), -- m_displayServer(new XorgDisplayServer(this)), - m_seat(parent), - m_socketServer(new SocketServer(this)), - m_greeter(new Greeter(this)) { -@@ -64,6 +64,32 @@ namespace SDDM { - // Allocate vt - m_terminalId = VirtualTerminal::setUpNewVt(); - -+ // Save display server type -+ const QString &displayServerType = mainConfig.DisplayServer.get().toLower(); -+ if (displayServerType == QLatin1String("x11")) -+ m_displayServerType = X11DisplayServerType; -+ else if (displayServerType == QStringLiteral("x11-user")) -+ m_displayServerType = X11UserDisplayServerType; -+ else { -+ qWarning("\"%s\" is an invalid value for General.DisplayServer: fall back to \"x11\"", -+ qPrintable(displayServerType)); -+ m_displayServerType = X11DisplayServerType; -+ } -+ -+ // Create display server -+ switch (m_displayServerType) { -+ case X11DisplayServerType: -+ m_displayServer = new XorgDisplayServer(this); -+ break; -+ case X11UserDisplayServerType: -+ m_displayServer = new XorgUserDisplayServer(this); -+ m_greeter->setDisplayServerCommand(XorgUserDisplayServer::command(this)); -+ break; -+ } -+ -+ // Print what VT we are using for more information -+ qDebug("Using VT %d", m_terminalId); -+ - // respond to authentication requests - m_auth->setVerbose(true); - connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged); -@@ -89,6 +115,16 @@ namespace SDDM { - stop(); - } - -+ Display::DisplayServerType Display::displayServerType() const -+ { -+ return m_displayServerType; -+ } -+ -+ DisplayServer *Display::displayServer() const -+ { -+ return m_displayServer; -+ } -+ - QString Display::displayId() const { - return m_displayServer->display(); - } -@@ -181,7 +217,8 @@ namespace SDDM { - - // set greeter params - m_greeter->setDisplay(this); -- m_greeter->setAuthPath(qobject_cast<XorgDisplayServer *>(m_displayServer)->authPath()); -+ if (qobject_cast<XorgDisplayServer *>(m_displayServer)) -+ m_greeter->setAuthPath(qobject_cast<XorgDisplayServer *>(m_displayServer)->authPath()); - m_greeter->setSocket(m_socketServer->socketAddress()); - m_greeter->setTheme(findGreeterTheme()); - -@@ -317,20 +354,16 @@ namespace SDDM { - // last session later, in slotAuthenticationFinished() - m_sessionName = session.fileName(); - -+ // New VT -+ m_lastSession.setVt(VirtualTerminal::setUpNewVt()); -+ - // some information - qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec(); - - QProcessEnvironment env; - env.insert(session.additionalEnv()); - -- if (seat()->name() == QLatin1String("seat0")) { -- // Use the greeter VT, for wayland sessions the helper overwrites this -- env.insert(QStringLiteral("XDG_VTNR"), QString::number(terminalId())); -- } -- - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); -- if (session.xdgSessionType() == QLatin1String("x11")) -- env.insert(QStringLiteral("DISPLAY"), name()); - env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name())); - env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); - env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); -@@ -338,10 +371,16 @@ namespace SDDM { - env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user")); - env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType()); - env.insert(QStringLiteral("XDG_SEAT"), seat()->name()); -+ env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_lastSession.vt())); - env.insert(QStringLiteral("XDG_SESSION_DESKTOP"), session.desktopNames()); - - m_auth->insertEnvironment(env); - -+ if (session.xdgSessionType() == QLatin1String("x11")) -+ m_auth->setDisplayServerCommand(XorgUserDisplayServer::command(this)); -+ else -+ m_auth->setDisplayServerCommand(QStringLiteral()); -+ - m_auth->setUser(user); - if (m_reuseSessionId.isNull()) { - m_auth->setSession(session.exec()); -@@ -358,7 +397,8 @@ namespace SDDM { - manager.UnlockSession(m_reuseSessionId); - manager.ActivateSession(m_reuseSessionId); - } else { -- m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie()); -+ if (qobject_cast<XorgDisplayServer *>(m_displayServer)) -+ m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie()); - } - - // save last user and last session -@@ -411,6 +451,10 @@ namespace SDDM { - // greeter - if (status != Auth::HELPER_AUTH_ERROR) - stop(); -+ -+ // Start the greeter again as soon as the user session is closed -+ if (m_auth->user() != QLatin1String("sddm")) -+ m_greeter->start(); - } - - void Display::slotRequestChanged() { -diff --git a/src/daemon/Display.h b/src/daemon/Display.h -index 61dd9f630..a31542cda 100644 ---- a/src/daemon/Display.h -+++ b/src/daemon/Display.h -@@ -41,9 +41,18 @@ namespace SDDM { - Q_OBJECT - Q_DISABLE_COPY(Display) - public: -+ enum DisplayServerType { -+ X11DisplayServerType, -+ X11UserDisplayServerType -+ }; -+ Q_ENUM(DisplayServerType) -+ - explicit Display(Seat *parent); - ~Display(); - -+ DisplayServerType displayServerType() const; -+ DisplayServer *displayServer() const; -+ - QString displayId() const; - const int terminalId() const; - -@@ -76,6 +85,8 @@ namespace SDDM { - void startAuth(const QString &user, const QString &password, - const Session &session); - -+ DisplayServerType m_displayServerType = X11DisplayServerType; -+ - bool m_relogin { true }; - bool m_started { false }; - -diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp -index 436ecc3d5..158d8bbbe 100644 ---- a/src/daemon/Greeter.cpp -+++ b/src/daemon/Greeter.cpp -@@ -27,6 +27,7 @@ - #include "ThemeConfig.h" - #include "ThemeMetadata.h" - #include "Display.h" -+#include "XorgUserDisplayServer.h" - - #include <QtCore/QDebug> - #include <QtCore/QProcess> -@@ -71,6 +72,16 @@ namespace SDDM { - } - } - -+ QString Greeter::displayServerCommand() const -+ { -+ return m_displayServerCmd; -+ } -+ -+ void Greeter::setDisplayServerCommand(const QString &cmd) -+ { -+ m_displayServerCmd = cmd; -+ } -+ - bool Greeter::start() { - // check flag - if (m_started) -@@ -111,17 +122,16 @@ namespace SDDM { - // log message - qDebug() << "Greeter starting..."; - -- // set process environment -+ args << QStringLiteral("--test-mode"); -+ -+ // set process environment - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display->name()); - env.insert(QStringLiteral("XAUTHORITY"), m_authPath); - env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); -- env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get()); - m_process->setProcessEnvironment(env); - - // start greeter -- if (daemonApp->testing()) -- args << QStringLiteral("--test-mode"); - m_process->start(QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)), args); - - //if we fail to start bail immediately, and don't block in waitForStarted -@@ -149,11 +159,12 @@ namespace SDDM { - m_auth->setVerbose(true); - connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); - connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); -+ connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); - connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); - connect(m_auth, &Auth::info, this, &Greeter::authInfo); - connect(m_auth, &Auth::error, this, &Greeter::authError); - -- // greeter command -+ // command - QStringList cmd; - cmd << QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)) - << args; -@@ -173,8 +184,6 @@ namespace SDDM { - }, sysenv, env); - - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); -- env.insert(QStringLiteral("DISPLAY"), m_display->name()); -- env.insert(QStringLiteral("XAUTHORITY"), m_authPath); - env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); - env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name()); - env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name())); -@@ -183,11 +192,10 @@ namespace SDDM { - env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId())); - env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter")); - env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType()); -- env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get()); -- -- //some themes may use KDE components and that will automatically load KDE's crash handler which we don't want -- //counterintuitively setting this env disables that handler -- env.insert(QStringLiteral("KDE_DEBUG"), QStringLiteral("1")); -+ if (m_display->displayServerType() == Display::X11DisplayServerType) { -+ env.insert(QStringLiteral("DISPLAY"), m_display->name()); -+ env.insert(QStringLiteral("XAUTHORITY"), m_authPath); -+ } - m_auth->insertEnvironment(env); - - // log message -@@ -195,6 +203,7 @@ namespace SDDM { - - // start greeter - m_auth->setUser(QStringLiteral("sddm")); -+ m_auth->setDisplayServerCommand(m_displayServerCmd); - m_auth->setGreeter(true); - m_auth->setSession(cmd.join(QLatin1Char(' '))); - m_auth->start(); -@@ -261,6 +270,14 @@ namespace SDDM { - qDebug() << "Greeter session failed to start"; - } - -+ void Greeter::onDisplayServerReady(const QString &displayName) -+ { -+ auto *displayServer = m_display->displayServer(); -+ auto *xorgUser = qobject_cast<XorgUserDisplayServer *>(displayServer); -+ if (xorgUser) -+ xorgUser->setDisplayName(displayName); -+ } -+ - void Greeter::onHelperFinished(Auth::HelperExitStatus status) { - // reset flag - m_started = false; -diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h -index 7391a3597..bf472375f 100644 ---- a/src/daemon/Greeter.h -+++ b/src/daemon/Greeter.h -@@ -43,6 +43,9 @@ namespace SDDM { - void setSocket(const QString &socket); - void setTheme(const QString &theme); - -+ QString displayServerCommand() const; -+ void setDisplayServerCommand(const QString &cmd); -+ - public slots: - bool start(); - void stop(); -@@ -51,6 +54,7 @@ namespace SDDM { - private slots: - void onRequestChanged(); - void onSessionStarted(bool success); -+ void onDisplayServerReady(const QString &displayName); - void onHelperFinished(Auth::HelperExitStatus status); - void onReadyReadStandardOutput(); - void onReadyReadStandardError(); -@@ -64,6 +68,7 @@ namespace SDDM { - QString m_authPath; - QString m_socket; - QString m_themePath; -+ QString m_displayServerCmd; - ThemeMetadata *m_metadata { nullptr }; - ThemeConfig *m_themeConfig { nullptr }; - -diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp -index 331adcda7..fc61ee2dd 100644 ---- a/src/daemon/XorgDisplayServer.cpp -+++ b/src/daemon/XorgDisplayServer.cpp -@@ -41,31 +41,9 @@ - - namespace SDDM { - XorgDisplayServer::XorgDisplayServer(Display *parent) : DisplayServer(parent) { -- // get auth directory -- QString authDir = QStringLiteral(RUNTIME_DIR); -- -- // use "." as authdir in test mode - if (daemonApp->testing()) -- authDir = QStringLiteral("."); -- -- // create auth dir if not existing -- QDir().mkpath(authDir); -- -- // set auth path -- m_authPath = QStringLiteral("%1/%2").arg(authDir).arg(QUuid::createUuid().toString()); -- -- // generate cookie -- std::random_device rd; -- std::mt19937 gen(rd()); -- std::uniform_int_distribution<> dis(0, 15); -- -- // resever 32 bytes -- m_cookie.reserve(32); -- -- // create a random hexadecimal number -- const char *digits = "0123456789abcdef"; -- for (int i = 0; i < 32; ++i) -- m_cookie[i] = digits[dis(gen)]; -+ m_xauth.setAuthDirectory(QStringLiteral(".")); -+ m_xauth.setup(); - } - - XorgDisplayServer::~XorgDisplayServer() { -@@ -76,41 +54,16 @@ namespace SDDM { - return m_display; - } - -- const QString &XorgDisplayServer::authPath() const { -- return m_authPath; -+ QString XorgDisplayServer::authPath() const { -+ return m_xauth.authPath(); - } - - QString XorgDisplayServer::sessionType() const { - return QStringLiteral("x11"); - } - -- const QString &XorgDisplayServer::cookie() const { -- return m_cookie; -- } -- -- bool XorgDisplayServer::addCookie(const QString &file) { -- // log message -- qDebug() << "Adding cookie to" << file; -- -- // Touch file -- QFile file_handler(file); -- file_handler.open(QIODevice::Append); -- file_handler.close(); -- -- QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file); -- -- // execute xauth -- FILE *fp = popen(qPrintable(cmd), "w"); -- -- // check file -- if (!fp) -- return false; -- fprintf(fp, "remove %s\n", qPrintable(m_display)); -- fprintf(fp, "add %s . %s\n", qPrintable(m_display), qPrintable(m_cookie)); -- fprintf(fp, "exit\n"); -- -- // close pipe -- return pclose(fp) == 0; -+ QString XorgDisplayServer::cookie() const { -+ return m_xauth.cookie(); - } - - bool XorgDisplayServer::start() { -@@ -136,7 +89,7 @@ namespace SDDM { - // For the X server's copy, the display number doesn't matter. - // An empty file would result in no access control! - m_display = QStringLiteral(":0"); -- if(!addCookie(m_authPath)) { -+ if(!m_xauth.addCookie(m_display)) { - qCritical() << "Failed to write xauth file"; - return false; - } -@@ -159,18 +112,15 @@ namespace SDDM { - process->setProgram(mainConfig.X11.ServerPath.get()); - args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts) - << QStringLiteral("-background") << QStringLiteral("none") -- << QStringLiteral("-seat") << displayPtr()->seat()->name(); -- -- if (displayPtr()->seat()->name() == QLatin1String("seat0")) { -- args << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); -- } -+ << QStringLiteral("-seat") << displayPtr()->seat()->name() -+ << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); - } else { - process->setProgram(mainConfig.X11.XephyrPath.get()); - args << QStringLiteral("-br") - << QStringLiteral("-screen") << QStringLiteral("800x600"); - } - -- args << QStringLiteral("-auth") << m_authPath -+ args << QStringLiteral("-auth") << m_xauth.authPath() - << QStringLiteral("-noreset") - << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); - -@@ -222,13 +172,13 @@ namespace SDDM { - // The file is also used by the greeter, which does care about the - // display number. Write the proper entry, if it's different. - if(m_display != QStringLiteral(":0")) { -- if(!addCookie(m_authPath)) { -+ if(!m_xauth.addCookie(m_display)) { - qCritical() << "Failed to write xauth file"; - stop(); - return false; - } - } -- changeOwner(m_authPath); -+ changeOwner(m_xauth.authPath()); - - emit started(); - -@@ -297,7 +247,7 @@ namespace SDDM { - displayStopScript = nullptr; - - // remove authority file -- QFile::remove(m_authPath); -+ QFile::remove(m_xauth.authPath()); - - // emit signal - emit stopped(); -@@ -316,7 +266,7 @@ namespace SDDM { - env.insert(QStringLiteral("DISPLAY"), m_display); - env.insert(QStringLiteral("HOME"), QStringLiteral("/")); - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); -- env.insert(QStringLiteral("XAUTHORITY"), m_authPath); -+ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); - env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); - env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); - setCursor->setProcessEnvironment(env); -diff --git a/src/daemon/XorgDisplayServer.h b/src/daemon/XorgDisplayServer.h -index e97a0b531..ebf189920 100644 ---- a/src/daemon/XorgDisplayServer.h -+++ b/src/daemon/XorgDisplayServer.h -@@ -22,6 +22,7 @@ - #define SDDM_XORGDISPLAYSERVER_H - - #include "DisplayServer.h" -+#include "XAuth.h" - - class QProcess; - -@@ -34,13 +35,11 @@ namespace SDDM { - ~XorgDisplayServer(); - - const QString &display() const; -- const QString &authPath() const; -+ QString authPath() const; - - QString sessionType() const; - -- const QString &cookie() const; -- -- bool addCookie(const QString &file); -+ QString cookie() const; - - public slots: - bool start(); -@@ -49,8 +48,7 @@ namespace SDDM { - void setupDisplay(); - - private: -- QString m_authPath; -- QString m_cookie; -+ XAuth m_xauth; - - QProcess *process { nullptr }; - -diff --git a/src/daemon/XorgUserDisplayServer.cpp b/src/daemon/XorgUserDisplayServer.cpp -new file mode 100644 -index 000000000..1e7c008a4 ---- /dev/null -+++ b/src/daemon/XorgUserDisplayServer.cpp -@@ -0,0 +1,102 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* -+* 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 "Configuration.h" -+#include "DaemonApp.h" -+#include "Display.h" -+#include "Seat.h" -+#include "XorgUserDisplayServer.h" -+ -+namespace SDDM { -+ -+XorgUserDisplayServer::XorgUserDisplayServer(Display *parent) -+ : DisplayServer(parent) -+{ -+} -+ -+XorgUserDisplayServer::~XorgUserDisplayServer() -+{ -+ stop(); -+} -+ -+QString XorgUserDisplayServer::sessionType() const -+{ -+ return QStringLiteral("x11"); -+} -+ -+void XorgUserDisplayServer::setDisplayName(const QString &displayName) -+{ -+ m_display = displayName; -+} -+ -+QString XorgUserDisplayServer::command(Display *display) -+{ -+ QStringList args; -+ -+ if (daemonApp->testing()) { -+ args << mainConfig.X11.XephyrPath.get() -+ << QStringLiteral("-br") -+ << QStringLiteral("-screen") << QStringLiteral("800x600"); -+ } else { -+ args << mainConfig.X11.ServerPath.get() -+ << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) -+ << QStringLiteral("-background") << QStringLiteral("none") -+ << QStringLiteral("-seat") << display->seat()->name() -+ << QStringLiteral("-noreset") -+ << QStringLiteral("-keeptty") -+ << QStringLiteral("-novtswitch") -+ << QStringLiteral("-verbose") << QStringLiteral("3"); -+ } -+ -+ return args.join(QLatin1Char(' ')); -+} -+ -+bool XorgUserDisplayServer::start() -+{ -+ // Check flag -+ if (m_started) -+ return false; -+ -+ // Set flag -+ m_started = true; -+ emit started(); -+ -+ return true; -+} -+ -+void XorgUserDisplayServer::stop() -+{ -+ // Check flag -+ if (!m_started) -+ return; -+ -+ // Reset flag -+ m_started = false; -+ emit stopped(); -+} -+ -+void XorgUserDisplayServer::finished() -+{ -+} -+ -+void XorgUserDisplayServer::setupDisplay() -+{ -+} -+ -+} // namespace SDDM -diff --git a/src/daemon/XorgUserDisplayServer.h b/src/daemon/XorgUserDisplayServer.h -new file mode 100644 -index 000000000..aa7cbe4a8 ---- /dev/null -+++ b/src/daemon/XorgUserDisplayServer.h -@@ -0,0 +1,53 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* -+* 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 SDDM_XORGUSERDISPLAYSERVER_H -+#define SDDM_XORGUSERDISPLAYSERVER_H -+ -+#include "DisplayServer.h" -+#include "XAuth.h" -+ -+class QProcess; -+ -+namespace SDDM { -+ -+class XorgUserDisplayServer : public DisplayServer -+{ -+ Q_OBJECT -+ Q_DISABLE_COPY(XorgUserDisplayServer) -+public: -+ explicit XorgUserDisplayServer(Display *parent); -+ ~XorgUserDisplayServer(); -+ -+ QString sessionType() const; -+ -+ void setDisplayName(const QString &displayName); -+ -+ static QString command(Display *display); -+ -+public Q_SLOTS: -+ bool start(); -+ void stop(); -+ void finished(); -+ void setupDisplay(); -+}; -+ -+} // namespace SDDM -+ -+#endif // SDDM_XORGUSERDISPLAYSERVER_H -diff --git a/src/greeter/GreeterApp.cpp b/src/greeter/GreeterApp.cpp -index 01f53fafb..b7f740f41 100644 ---- a/src/greeter/GreeterApp.cpp -+++ b/src/greeter/GreeterApp.cpp -@@ -337,6 +337,15 @@ int main(int argc, char **argv) - QSurfaceFormat::setDefaultFormat(format); - } - -+ // Some themes may use KDE components and that will automatically load KDE's -+ // crash handler which we don't want counterintuitively setting this env -+ // disables that handler -+ qputenv("KDE_DEBUG", "1"); -+ -+ // Qt IM module -+ if (!SDDM::mainConfig.InputMethod.get().isEmpty()) -+ qputenv("QT_IM_MODULE", SDDM::mainConfig.InputMethod.get().toLocal8Bit().constData()); -+ - QGuiApplication app(argc, argv); - - QCommandLineParser parser; -diff --git a/src/helper/Backend.cpp b/src/helper/Backend.cpp -index a324b39fb..9a36a62ba 100644 ---- a/src/helper/Backend.cpp -+++ b/src/helper/Backend.cpp -@@ -54,6 +54,11 @@ namespace SDDM { - m_autologin = on; - } - -+ void Backend::setDisplayServer(bool on) -+ { -+ m_displayServer = on; -+ } -+ - void Backend::setGreeter(bool on) { - m_greeter = on; - } -diff --git a/src/helper/Backend.h b/src/helper/Backend.h -index b790e0011..915d09ca8 100644 ---- a/src/helper/Backend.h -+++ b/src/helper/Backend.h -@@ -36,6 +36,7 @@ namespace SDDM { - static Backend *get(HelperApp *parent); - - void setAutologin(bool on = true); -+ void setDisplayServer(bool on = true); - void setGreeter(bool on = true); - - public slots: -@@ -50,6 +51,7 @@ namespace SDDM { - Backend(HelperApp *parent); - HelperApp *m_app; - bool m_autologin { false }; -+ bool m_displayServer = false; - bool m_greeter { false }; - }; - } -diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt -index 8914ea757..24f36eb97 100644 ---- a/src/helper/CMakeLists.txt -+++ b/src/helper/CMakeLists.txt -@@ -10,9 +10,13 @@ set(HELPER_SOURCES - ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp - ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp - ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp -+ ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp -+ ${CMAKE_SOURCE_DIR}/src/common/XAuth.h - Backend.cpp - HelperApp.cpp - UserSession.cpp -+ xorguserhelper.cpp -+ xorguserhelper.h - ) - - # Different implementations of the VT switching code -diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp -index 3f92f1d8f..059e9c156 100644 ---- a/src/helper/HelperApp.cpp -+++ b/src/helper/HelperApp.cpp -@@ -88,6 +88,16 @@ namespace SDDM { - m_user = args[pos + 1]; - } - -+ if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { -+ if (pos >= args.length() - 1) { -+ qCritical() << "This application is not supposed to be executed manually"; -+ exit(Auth::HELPER_OTHER_ERROR); -+ return; -+ } -+ m_session->setDisplayServerCommand(args[pos + 1]); -+ m_backend->setDisplayServer(true); -+ } -+ - if ((pos = args.indexOf(QStringLiteral("--autologin"))) >= 0) { - m_backend->setAutologin(true); - } -@@ -103,7 +113,7 @@ namespace SDDM { - } - - connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); -- connect(m_session, QOverload<int>::of(&QProcess::finished), this, &HelperApp::sessionFinished); -+ connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); - m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered); - } - -@@ -131,11 +141,6 @@ namespace SDDM { - - if (!m_session->path().isEmpty()) { - env.insert(m_session->processEnvironment()); -- // Allocate a new VT for the wayland session -- if(env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { -- int vtNumber = VirtualTerminal::setUpNewVt(); -- env.insert(QStringLiteral("XDG_VTNR"), QString::number(vtNumber)); -- } - m_session->setProcessEnvironment(env); - - if (!m_backend->openSession()) { -@@ -216,6 +221,19 @@ namespace SDDM { - } - } - -+ void HelperApp::displayServerStarted(const QString &displayName) -+ { -+ Msg m = Msg::MSG_UNKNOWN; -+ SafeDataStream str(m_socket); -+ str << Msg::DISPLAY_SERVER_STARTED << displayName; -+ str.send(); -+ str.receive(); -+ str >> m; -+ if (m != DISPLAY_SERVER_STARTED) { -+ qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m; -+ } -+ } -+ - UserSession *HelperApp::session() { - return m_session; - } -diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h -index 632435ee5..1a75c2d67 100644 ---- a/src/helper/HelperApp.h -+++ b/src/helper/HelperApp.h -@@ -48,6 +48,7 @@ namespace SDDM { - void error(const QString &message, Auth::Error type); - QProcessEnvironment authenticated(const QString &user); - void sessionOpened(bool success); -+ void displayServerStarted(const QString &displayName); - - private slots: - void setUp(); -diff --git a/src/helper/UserSession.cpp b/src/helper/UserSession.cpp -index 8e36f2113..f02a42c21 100644 ---- a/src/helper/UserSession.cpp -+++ b/src/helper/UserSession.cpp -@@ -19,10 +19,14 @@ - * - */ - -+#include <QSocketNotifier> -+ - #include "Configuration.h" - #include "UserSession.h" - #include "HelperApp.h" - #include "VirtualTerminal.h" -+#include "XAuth.h" -+#include "xorguserhelper.h" - - #include <sys/types.h> - #include <sys/ioctl.h> -@@ -37,11 +41,19 @@ - - namespace SDDM { - UserSession::UserSession(HelperApp *parent) -- : QProcess(parent) { -- } -- -- UserSession::~UserSession() { -- -+ : QObject(parent) -+ , m_process(new QProcess(this)) -+ , m_xorgUser(new XOrgUserHelper(this)) -+ { -+ connect(m_process, QOverload<int>::of(&QProcess::finished), this, &UserSession::finished); -+ connect(m_xorgUser, &XOrgUserHelper::displayChanged, this, [this, parent](const QString &display) { -+ auto env = processEnvironment(); -+ env.insert(QStringLiteral("DISPLAY"), m_xorgUser->display()); -+ env.insert(QStringLiteral("XAUTHORITY"), m_xorgUser->xauthPath()); -+ setProcessEnvironment(env); -+ -+ parent->displayServerStarted(display); -+ }); - } - - bool UserSession::start() { -@@ -49,21 +61,68 @@ namespace SDDM { - - setup(); - -- if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { -- QProcess::start(m_path); -- } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { -- const QString cmd = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); -- qInfo() << "Starting:" << cmd; -- QProcess::start(cmd); -+ if (!m_displayServerCmd.isEmpty()) { -+ m_xorgUser->setEnvironment(env); -+ if (!m_xorgUser->start(m_displayServerCmd)) -+ return false; -+ } -+ -+ if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { -+ if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { -+ qInfo() << "Starting X11 greeter session:" << m_path; -+ auto args = QProcess::splitCommand(m_path); -+ const auto program = args.takeFirst(); -+ m_process->start(program, args); -+ } else { -+ const QString cmd = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); -+ qInfo() << "Starting X11 user session:" << cmd; -+ m_process->start(mainConfig.X11.SessionCommand.get(), QStringList{m_path}); -+ } - } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - const QString cmd = QStringLiteral("%1 %2").arg(mainConfig.Wayland.SessionCommand.get()).arg(m_path); -- qInfo() << "Starting:" << cmd; -- QProcess::start(cmd); -+ qInfo() << "Starting Wayland user session:" << cmd; -+ m_process->start(mainConfig.Wayland.SessionCommand.get(), QStringList{m_path}); - } else { - qCritical() << "Unable to run user session: unknown session type"; - } - -- return waitForStarted(); -+ if (m_process->waitForStarted()) { -+ int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt(); -+ VirtualTerminal::jumpToVt(vtNumber, true); -+ return true; -+ } -+ -+ return false; -+ } -+ -+ void UserSession::stop() -+ { -+ m_process->terminate(); -+ if (!m_process->waitForFinished(5000)) -+ m_process->kill(); -+ -+ if (!m_displayServerCmd.isEmpty()) -+ m_xorgUser->stop(); -+ } -+ -+ QProcessEnvironment UserSession::processEnvironment() const -+ { -+ return m_process->processEnvironment(); -+ } -+ -+ void UserSession::setProcessEnvironment(const QProcessEnvironment &env) -+ { -+ m_process->setProcessEnvironment(env); -+ } -+ -+ QString UserSession::displayServerCommand() const -+ { -+ return m_displayServerCmd; -+ } -+ -+ void UserSession::setDisplayServerCommand(const QString &command) -+ { -+ m_displayServerCmd = command; - } - - void UserSession::setPath(const QString& path) { -@@ -74,13 +133,22 @@ namespace SDDM { - return m_path; - } - -+ qint64 UserSession::processId() const -+ { -+ return m_process->processId(); -+ } -+ - void UserSession::setupChildProcess() { - // Session type - QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE")); -- -- // For Wayland sessions we leak the VT into the session as stdin so -- // that it stays open without races -- if (sessionType == QLatin1String("wayland")) { -+ QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")); -+ const bool hasDisplayServer = !m_displayServerCmd.isEmpty(); -+ const bool x11UserSession = sessionType == QLatin1String("x11") && sessionClass == QLatin1String("user"); -+ const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user"); -+ -+ // When the display server is part of the session, we leak the VT into -+ // the session as stdin so that it stays open without races -+ if (hasDisplayServer || waylandUserSession) { - // open VT and get the fd - int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt(); - QString ttyString = QStringLiteral("/dev/tty%1").arg(vtNumber); -@@ -226,74 +294,56 @@ namespace SDDM { - qCritical() << "verify directory exist and has sufficient permissions"; - exit(Auth::HELPER_OTHER_ERROR); - } -- const QString homeDir = QString::fromLocal8Bit(pw.pw_dir); -- -- //we cannot use setStandardError file as this code is run in the child process -- //we want to redirect after we setuid so that the log file is owned by the user -- -- // determine stderr log file based on session type -- QString sessionLog = QStringLiteral("%1/%2") -- .arg(homeDir) -- .arg(sessionType == QLatin1String("x11") -- ? mainConfig.X11.SessionLogFile.get() -- : mainConfig.Wayland.SessionLogFile.get()); -- -- // create the path -- QFileInfo finfo(sessionLog); -- QDir().mkpath(finfo.absolutePath()); -- -- //swap the stderr pipe of this subprcess into a file -- int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600); -- if (fd >= 0) -- { -- dup2 (fd, STDERR_FILENO); -- ::close(fd); -- } else { -- qWarning() << "Could not open stderr to" << sessionLog; -- } -- -- //redirect any stdout to /dev/null -- fd = ::open("/dev/null", O_WRONLY); -- if (fd >= 0) -- { -- dup2 (fd, STDOUT_FILENO); -- ::close(fd); -- } else { -- qWarning() << "Could not redirect stdout"; -- } - -- // set X authority for X11 sessions only -- if (sessionType != QLatin1String("x11")) -- return; -- QString cookie = qobject_cast<HelperApp*>(parent())->cookie(); -- if (!cookie.isEmpty()) { -- QString file = processEnvironment().value(QStringLiteral("XAUTHORITY")); -- QString display = processEnvironment().value(QStringLiteral("DISPLAY")); -- qDebug() << "Adding cookie to" << file; -+ if (sessionClass != QLatin1String("greeter")) { -+ //we cannot use setStandardError file as this code is run in the child process -+ //we want to redirect after we setuid so that the log file is owned by the user - -+ // determine stderr log file based on session type -+ QString sessionLog = QStringLiteral("%1/%2") -+ .arg(QString::fromLocal8Bit(pw.pw_dir)) -+ .arg(sessionType == QLatin1String("x11") -+ ? mainConfig.X11.SessionLogFile.get() -+ : mainConfig.Wayland.SessionLogFile.get()); - - // create the path -- QFileInfo finfo(file); -+ QFileInfo finfo(sessionLog); - QDir().mkpath(finfo.absolutePath()); - -- QFile file_handler(file); -- file_handler.open(QIODevice::Append); -- file_handler.close(); -+ //swap the stderr pipe of this subprcess into a file -+ int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600); -+ if (fd >= 0) -+ { -+ dup2 (fd, STDERR_FILENO); -+ ::close(fd); -+ } else { -+ qWarning() << "Could not open stderr to" << sessionLog; -+ } - -- QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file); -+ //redirect any stdout to /dev/null -+ fd = ::open("/dev/null", O_WRONLY); -+ if (fd >= 0) -+ { -+ dup2 (fd, STDOUT_FILENO); -+ ::close(fd); -+ } else { -+ qWarning() << "Could not redirect stdout"; -+ } -+ } - -- // execute xauth -- FILE *fp = popen(qPrintable(cmd), "w"); -+ // set X authority for X11 sessions only -+ if (x11UserSession) { -+ QString cookie = qobject_cast<HelperApp*>(parent())->cookie(); -+ if (!cookie.isEmpty()) { -+ QString file = processEnvironment().value(QStringLiteral("XAUTHORITY")); -+ QString display = processEnvironment().value(QStringLiteral("DISPLAY")); - -- // check file -- if (!fp) -- return; -- fprintf(fp, "remove %s\n", qPrintable(display)); -- fprintf(fp, "add %s . %s\n", qPrintable(display), qPrintable(cookie)); -- fprintf(fp, "exit\n"); -+ // Create the path -+ QFileInfo finfo(file); -+ QDir().mkpath(finfo.absolutePath()); - -- // close pipe -- pclose(fp); -+ XAuth::addCookieToFile(display, file, cookie); -+ } - } - } - } -diff --git a/src/helper/UserSession.h b/src/helper/UserSession.h -index c2383f068..11bff1d1b 100644 ---- a/src/helper/UserSession.h -+++ b/src/helper/UserSession.h -@@ -23,41 +23,55 @@ - #define SDDM_AUTH_SESSION_H - - #include <QtCore/QObject> --#include <QtCore/QString> - #include <QtCore/QProcess> - - namespace SDDM { - class HelperApp; -- class UserSession : public QProcess -+ class XOrgUserHelper; -+ class UserSession : public QObject - { - Q_OBJECT - public: - explicit UserSession(HelperApp *parent); -- virtual ~UserSession(); - - bool start(); -+ void stop(); -+ -+ QProcessEnvironment processEnvironment() const; -+ void setProcessEnvironment(const QProcessEnvironment &env); -+ -+ QString displayServerCommand() const; -+ void setDisplayServerCommand(const QString &command); - - void setPath(const QString &path); - QString path() const; - - /*! - \brief Sets m_cachedProcessId. Needed for getting the PID of a finished UserSession - and calling HelperApp::utmpLogout - \param pid The process ID - */ - void setCachedProcessId(qint64 pid); - - /*! - \brief Gets m_cachedProcessId - \return The cached process ID - */ - qint64 cachedProcessId(); - -+ qint64 processId() const; -+ -+ Q_SIGNALS: -+ void finished(int exitCode); -+ - protected: - void setupChildProcess(); - - private: - QString m_path { }; - qint64 m_cachedProcessId; -+ QProcess *m_process = nullptr; -+ XOrgUserHelper *m_xorgUser = nullptr; -+ QString m_displayServerCmd; - }; - } - -diff --git a/src/helper/backend/PamBackend.cpp b/src/helper/backend/PamBackend.cpp -index f86d77d63..c97056a66 100644 ---- a/src/helper/backend/PamBackend.cpp -+++ b/src/helper/backend/PamBackend.cpp -@@ -248,7 +248,9 @@ namespace SDDM { - } - - QProcessEnvironment sessionEnv = m_app->session()->processEnvironment(); -- if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { -+ const auto sessionType = sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")); -+ const auto sessionClass = sessionEnv.value(QStringLiteral("XDG_SESSION_CLASS")); -+ if (sessionType == QLatin1String("x11") && (sessionClass == QLatin1String("user") || !m_displayServer)) { - QString display = sessionEnv.value(QStringLiteral("DISPLAY")); - if (!display.isEmpty()) { - #ifdef PAM_XDISPLAY -@@ -256,7 +258,7 @@ namespace SDDM { - #endif - m_pam->setItem(PAM_TTY, qPrintable(display)); - } -- } else if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { -+ } else { - QString tty = QStringLiteral("/dev/tty%1").arg(sessionEnv.value(QStringLiteral("XDG_VTNR"))); - m_pam->setItem(PAM_TTY, qPrintable(tty)); - } -diff --git a/src/helper/xorguserhelper.cpp b/src/helper/xorguserhelper.cpp -new file mode 100644 -index 000000000..573ed101f ---- /dev/null -+++ b/src/helper/xorguserhelper.cpp -@@ -0,0 +1,252 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* -+* 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 <QCoreApplication> -+#include <QFile> -+#include <QStandardPaths> -+ -+#include "Configuration.h" -+ -+#include "xorguserhelper.h" -+ -+#include <fcntl.h> -+#include <unistd.h> -+ -+namespace SDDM { -+ -+XOrgUserHelper::XOrgUserHelper(QObject *parent) -+ : QObject(parent) -+{ -+} -+ -+QProcessEnvironment XOrgUserHelper::environment() const -+{ -+ return m_environment; -+} -+ -+void XOrgUserHelper::setEnvironment(const QProcessEnvironment &env) -+{ -+ m_environment = env; -+} -+ -+QString XOrgUserHelper::display() const -+{ -+ return m_display; -+} -+ -+QString XOrgUserHelper::xauthPath() const -+{ -+ return m_xauth.authPath(); -+} -+ -+bool XOrgUserHelper::start(const QString &cmd) -+{ -+ // Create xauthority -+ m_xauth.setAuthDirectory(m_environment.value(QStringLiteral("XDG_RUNTIME_DIR"))); -+ m_xauth.setup(); -+ -+ // Start server process -+ if (!startServer(cmd)) -+ return false; -+ -+ // Setup display -+ startDisplayCommand(); -+ -+ return true; -+} -+ -+void XOrgUserHelper::stop() -+{ -+ if (m_serverProcess) { -+ qInfo("Stopping server..."); -+ m_serverProcess->terminate(); -+ if (!m_serverProcess->waitForFinished(5000)) -+ m_serverProcess->kill(); -+ m_serverProcess->deleteLater(); -+ m_serverProcess = nullptr; -+ -+ displayFinished(); -+ } -+} -+ -+bool XOrgUserHelper::startProcess(const QString &cmd, -+ const QProcessEnvironment &env, -+ QProcess **p) -+{ -+ auto args = QProcess::splitCommand(cmd); -+ const auto program = args.takeFirst(); -+ -+ // Make sure to forward the input of this process into the Xorg -+ // server, otherwise it will complain that only console users are allowed -+ auto *process = new QProcess(this); -+ process->setInputChannelMode(QProcess::ForwardedInputChannel); -+ process->setProcessChannelMode(QProcess::ForwardedChannels); -+ process->setProcessEnvironment(env); -+ -+ connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), -+ process, [](int exitCode, QProcess::ExitStatus exitStatus) { -+ if (exitCode != 0 || exitStatus != QProcess::NormalExit) -+ QCoreApplication::instance()->quit(); -+ }); -+ -+ process->start(program, args); -+ if (!process->waitForStarted(10000)) { -+ qWarning("Failed to start \"%s\": %s", -+ qPrintable(cmd), -+ qPrintable(process->errorString())); -+ return false; -+ } -+ -+ if (p) -+ *p = process; -+ -+ return true; -+} -+ -+bool XOrgUserHelper::startServer(const QString &cmd) -+{ -+ QString serverCmd = cmd; -+ -+ // Create pipe for communicating with X server -+ // 0 == read from X, 1 == write to X -+ int pipeFds[2]; -+ if (::pipe(pipeFds) != 0) { -+ qCritical("Could not create pipe to start X server"); -+ return false; -+ } -+ -+ // Do not leak the read endpoint to the X server process -+ fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); -+ -+ // Server environment -+ // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges -+ // under Fedora and all distros that use their patch. -+ // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch -+ // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights -+ QProcessEnvironment serverEnv = m_environment; -+ serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1")); -+ -+ // Append xauth and display fd to the command -+ auto args = QStringList() -+ << QStringLiteral("-auth") << m_xauth.authPath() -+ << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); -+ -+ // Append VT from environment -+ args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR"))); -+ -+ // Log to stdout -+ args << QStringLiteral("-logfile") << QStringLiteral("/dev/null"); -+ -+ // Command string -+ serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' ')); -+ -+ // Start the server process -+ qInfo("Running server: %s", qPrintable(serverCmd)); -+ if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) { -+ ::close(pipeFds[0]); -+ return false; -+ } -+ -+ // Close the other side of pipe in our process, otherwise reading -+ // from it may stuck even X server exit -+ ::close(pipeFds[1]); -+ -+ // Read the display number from the pipe -+ QFile readPipe; -+ if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { -+ qCritical("Failed to open pipe to start X Server"); -+ ::close(pipeFds[0]); -+ return false; -+ } -+ QByteArray displayNumber = readPipe.readLine(); -+ if (displayNumber.size() < 2) { -+ // X server gave nothing (or a whitespace) -+ qCritical("Failed to read display number from pipe"); -+ ::close(pipeFds[0]); -+ return false; -+ } -+ displayNumber.prepend(QByteArray(":")); -+ displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace -+ m_display = QString::fromLocal8Bit(displayNumber); -+ qDebug("X11 display: %s", qPrintable(m_display)); -+ Q_EMIT displayChanged(m_display); -+ -+ // Generate xauthority file -+ // For the X server's copy, the display number doesn't matter. -+ // An empty file would result in no access control! -+ if (!m_xauth.addCookie(m_display)) { -+ qCritical("Failed to write xauth file"); -+ return false; -+ } -+ -+ // Close our pipe -+ ::close(pipeFds[0]); -+ -+ return true; -+} -+ -+void XOrgUserHelper::startDisplayCommand() -+{ -+ auto env = QProcessEnvironment::systemEnvironment(); -+ env.insert(QStringLiteral("DISPLAY"), m_display); -+ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); -+ -+ // Set cursor -+ qInfo("Setting default cursor..."); -+ QProcess *setCursor = nullptr; -+ if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) { -+ if (!setCursor->waitForFinished(1000)) { -+ qWarning() << "Could not setup default cursor"; -+ setCursor->kill(); -+ } -+ setCursor->deleteLater(); -+ } -+ -+ // Display setup script -+ auto cmd = mainConfig.X11.DisplayCommand.get(); -+ qInfo("Running display setup script: %s", qPrintable(cmd)); -+ QProcess *displayScript = nullptr; -+ if (startProcess(cmd, env, &displayScript)) { -+ if (!displayScript->waitForFinished(30000)) -+ displayScript->kill(); -+ displayScript->deleteLater(); -+ } -+} -+ -+void XOrgUserHelper::displayFinished() -+{ -+ auto env = QProcessEnvironment::systemEnvironment(); -+ env.insert(QStringLiteral("DISPLAY"), m_display); -+ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); -+ env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); -+ -+ auto cmd = mainConfig.X11.DisplayStopCommand.get(); -+ qInfo("Running display stop script: %s", qPrintable(cmd)); -+ QProcess *displayStopScript = nullptr; -+ if (startProcess(cmd, env, &displayStopScript)) { -+ if (!displayStopScript->waitForFinished(5000)) -+ displayStopScript->kill(); -+ displayStopScript->deleteLater(); -+ } -+ -+ // Remove xauthority file -+ QFile::remove(m_xauth.authPath()); -+} -+ -+} // namespace SDDM -diff --git a/src/helper/xorguserhelper.h b/src/helper/xorguserhelper.h -new file mode 100644 -index 000000000..73536965a ---- /dev/null -+++ b/src/helper/xorguserhelper.h -@@ -0,0 +1,64 @@ -+/*************************************************************************** -+* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -+* -+* This program is free software; you can redistribute it and/or modify -+* it under the terms of the GNU General Public License as published by -+* the Free Software Foundation; either version 2 of the License, or -+* (at your option) any later version. -+* -+* This program is distributed in the hope that it will be useful, -+* but WITHOUT ANY WARRANTY; without even the implied warranty of -+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+* GNU General Public License for more details. -+* -+* You should have received a copy of the GNU General Public License -+* along with this program; if not, write to the -+* Free Software Foundation, Inc., -+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+***************************************************************************/ -+ -+#ifndef XORGUSERHELPER_H -+#define XORGUSERHELPER_H -+ -+#include <QProcess> -+ -+#include "XAuth.h" -+ -+namespace SDDM { -+ -+class XOrgUserHelper : public QObject -+{ -+ Q_OBJECT -+ Q_PROPERTY(QString display READ display NOTIFY displayChanged) -+public: -+ explicit XOrgUserHelper(QObject *parent = nullptr); -+ -+ QProcessEnvironment environment() const; -+ void setEnvironment(const QProcessEnvironment &env); -+ -+ QString display() const; -+ -+ QString xauthPath() const; -+ -+ bool start(const QString &cmd); -+ void stop(); -+ -+Q_SIGNALS: -+ void displayChanged(const QString &display); -+ -+private: -+ QString m_display = QStringLiteral(":0"); -+ XAuth m_xauth; -+ QProcessEnvironment m_environment; -+ QProcess *m_serverProcess = nullptr; -+ -+ bool startProcess(const QString &cmd, const QProcessEnvironment &env, -+ QProcess **p = nullptr); -+ bool startServer(const QString &cmd); -+ void startDisplayCommand(); -+ void displayFinished(); -+}; -+ -+} // namespace SDDM -+ -+#endif // XORGUSERHELPER_H |