summaryrefslogtreecommitdiff
path: root/user/sddm/rootless-xorg.patch
diff options
context:
space:
mode:
Diffstat (limited to 'user/sddm/rootless-xorg.patch')
-rw-r--r--user/sddm/rootless-xorg.patch2012
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