From 40526154b709a62f45c832c02fb5d1ef56e16754 Mon Sep 17 00:00:00 2001
From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
Date: Fri, 19 Aug 2022 22:26:54 -0500
Subject: user/sddm: Update to 0.19.0

Fixes: #368, #646
---
 user/sddm/rootless-xorg.patch | 2012 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2012 insertions(+)
 create mode 100644 user/sddm/rootless-xorg.patch

(limited to 'user/sddm/rootless-xorg.patch')

diff --git a/user/sddm/rootless-xorg.patch b/user/sddm/rootless-xorg.patch
new file mode 100644
index 000000000..99a2e189d
--- /dev/null
+++ b/user/sddm/rootless-xorg.patch
@@ -0,0 +1,2012 @@
+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
-- 
cgit v1.2.3-70-g09d2