diff options
Diffstat (limited to 'user/sddm')
-rw-r--r-- | user/sddm/APKBUILD | 33 | ||||
-rw-r--r-- | user/sddm/allocate-vt.patch | 220 | ||||
-rw-r--r-- | user/sddm/authed.patch | 32 | ||||
-rw-r--r-- | user/sddm/autologin-type.patch | 31 | ||||
-rw-r--r-- | user/sddm/cleanup-xorg.patch | 63 | ||||
-rw-r--r-- | user/sddm/qt515.patch | 199 | ||||
-rw-r--r-- | user/sddm/retry.patch | 115 | ||||
-rw-r--r-- | user/sddm/revert-fedora.patch | 74 | ||||
-rw-r--r-- | user/sddm/rootless-xorg.patch | 2012 | ||||
-rw-r--r-- | user/sddm/session-env.patch | 122 | ||||
-rw-r--r-- | user/sddm/utmpx.patch | 8 | ||||
-rw-r--r-- | user/sddm/xephyr.patch | 171 |
12 files changed, 3071 insertions, 9 deletions
diff --git a/user/sddm/APKBUILD b/user/sddm/APKBUILD index 78ca0f8d5..00071648c 100644 --- a/user/sddm/APKBUILD +++ b/user/sddm/APKBUILD @@ -1,8 +1,8 @@ # Contributor: A. Wilcox <awilfox@adelielinux.org> # Maintainer: A. Wilcox <awilfox@adelielinux.org> pkgname=sddm -pkgver=0.18.1 -pkgrel=3 +pkgver=0.19.0 +pkgrel=0 pkgdesc="Simple Desktop Display Manager" url="https://github.com/sddm/sddm/" pkgusers="sddm" @@ -16,10 +16,21 @@ makedepends="cmake extra-cmake-modules qt5-qtbase-dev libxcb-dev upower-dev subpackages="$pkgname-lang $pkgname-openrc" install="sddm.post-install" langdir="/usr/share/sddm/translations" +# Patch files are in order of needed application. source="https://github.com/sddm/sddm/releases/download/v$pkgver/sddm-$pkgver.tar.xz pam-path-fix.patch - sddm.initd utmpx.patch + revert-fedora.patch + xephyr.patch + retry.patch + cleanup-xorg.patch + authed.patch + allocate-vt.patch + autologin-type.patch + session-env.patch + rootless-xorg.patch + qt515.patch + sddm.initd " build() { @@ -56,7 +67,17 @@ openrc() { depends="sddm xorg-server" } -sha512sums="ff0637600cda2f4da1f643f047f8ee822bd9651ae4ccbb614b9804175c97360ada7af93e07a7b63832f014ef6e7d1b5380ab2b8959f8024ea520fa5ff17efd60 sddm-0.18.1.tar.xz +sha512sums="0a40816bc105a1e930fec2d65fabff0ae7e27c641235d90e41f6fbaa86af4bb774a9e30f7548ce2c6c791e6d4f8195b02afddedca60a9e7c77447702e728edc3 sddm-0.19.0.tar.xz f0b4eb7ef0581701157f9decc637629156f36f6711b9a4bae517f94d7a1df614c81bbd891c918f07ac50e2a3d1519c43ccb9eefd80282c95dd79eca0e8d90904 pam-path-fix.patch -d603934552bad47edda458d7a4df2310e98bde74bdb3bf8588f5171b2a5d68814192b8dc8f5599b35402f9a747d519d985d4976e7aa50dabed445f99a112594c sddm.initd -c42d8b3edbc0ae7e3d5ea7bb0080c5c50e0569f0ea947e1ba17bc794c8c0d67a214e62aad7eba0a51791c44b29a3017692bbe738250c63cb2219891bb1313422 utmpx.patch" +b61c02e9d75c03c5a04a44bf53bf1d7df3afe6214b2c7f6da8a2b3a78c859443fb2af6ff748a9d369707e6eaab8c0e5f9872c35e52e602836795b7099330ba00 utmpx.patch +51a0b6d427da8ea8dfbeb043ae4eb47f6c55645d03ed3a88e7583c216c52916863d8184faa6402f348e6ca704098b180079f9c6d4faaca1855bad58f9cf5f531 revert-fedora.patch +65fb8d772ca149f3616cb9d106eebf246b65bc8c0d2e328bd361dcc2664afb6ae467f698ac656653c5092f3b2b5f14b2980f9f1f3929839bb0674c5c801b36a1 xephyr.patch +d47123e097e8ea918ba112332272dbc94a3f06a76dc01080fd80f5f945320e0647d8f84d4232d32650c9705e2228c054748ce7207492a4c7e6039655668a0440 retry.patch +5dd057acc054f905ad0b9b0924eeb792b417cfe9d506f1d138266b9dc38c229b1b32d0c2443e85db5bba90d8c64efcebfceac07bd742acf0d8c10d992e8b4e1a cleanup-xorg.patch +fea260015dfaf82e97ea2a77d7810e11f85503446ce6450ed9fe3072ce2151bb2ecac56fbc917a47b8ebcaf9fa51c077d4e798c4ef62919e18d1751fd7de92fc authed.patch +829397bdb1d5fd6887b7836d503b081a8827346aa775b702d4ea9946d3ff4374446175e462732e52e33e1a53481b99cbe652c44e031c79823d5fbf67e4b48c46 allocate-vt.patch +378a735946c93dc9fb746cc4d4cf436d728ec0f1fddd3fcac8797ef90acb0d6582e41ac0a91b96d3886695b8664627ef81822f531c67deb12085626206f27136 autologin-type.patch +0997ae1ea2f0b44835a397f107d73ae2eb0d492cbf97b3f446c388eebb6ec0b9255b2352a51c9e00ebf846c0fec61998895aba865e06814ee20ab9d526b3f353 session-env.patch +3941a27c143205f7cc5d2e4fee96bdf463319b85f412ef076eedc730f82c16f0e565c897beeddda04b678bf9040511d3900ef8f7452a6a6b8f224c597232194b rootless-xorg.patch +a7fba93e5eb78e2afca6106559cef64885d9be8ac5b69b2618af60d857c18a8ccac4800ca9698cc808e97039a9584dd073baf0a119dc8072d796a47001c52ee6 qt515.patch +d603934552bad47edda458d7a4df2310e98bde74bdb3bf8588f5171b2a5d68814192b8dc8f5599b35402f9a747d519d985d4976e7aa50dabed445f99a112594c sddm.initd" diff --git a/user/sddm/allocate-vt.patch b/user/sddm/allocate-vt.patch new file mode 100644 index 000000000..34e599cd3 --- /dev/null +++ b/user/sddm/allocate-vt.patch @@ -0,0 +1,220 @@ +From f0b6dec66a6fc8cc6e21da57f81190843b483f76 Mon Sep 17 00:00:00 2001 +From: Pier Luigi Fiorini <pierluigi.fiorini@liri.io> +Date: Sun, 28 Feb 2021 12:07:33 +0100 +Subject: [PATCH] Allocate VT for the display + +Replace the old crude algorithm to find the next available VT with a +more reliable method using the VT_OPENQRY ioctl. + +General.MinimumVT setting is now obsolete and it's no longer used. +--- + CMakeLists.txt | 4 ---- + data/man/sddm.conf.rst.in | 2 +- + src/common/Configuration.h | 1 - + src/common/Constants.h.in | 1 - + src/daemon/Display.cpp | 7 +++++-- + src/daemon/Display.h | 2 +- + src/daemon/Seat.cpp | 38 +++----------------------------------- + src/daemon/Seat.h | 3 +-- + 8 files changed, 11 insertions(+), 47 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e52e0e903..9614b4e1e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -141,7 +141,6 @@ if(SYSTEMD_FOUND) + string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SYSTEM_UNIT_DIR ${SYSTEMD_SYSTEM_UNIT_DIR}) + endif() + +- set(MINIMUM_VT 1) + set(HALT_COMMAND "/usr/bin/systemctl poweroff") + set(REBOOT_COMMAND "/usr/bin/systemctl reboot") + else() +@@ -159,7 +158,6 @@ if(ELOGIND_FOUND) + add_definitions(-DHAVE_ELOGIND) + set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_ELOGIND) + +- set(MINIMUM_VT 7) + set(HALT_COMMAND "/usr/bin/loginctl poweroff") + set(REBOOT_COMMAND "/usr/bin/loginctl reboot") + endif() +@@ -171,10 +169,8 @@ if (NOT ELOGIND_FOUND AND NOT SYSTEMD_FOUND) + # commands for shutdown and reboot. On FreeBSD, there are + # normally more getty's running than on Linux. + if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") +- set(MINIMUM_VT 9) + set(HALT_COMMAND "/sbin/shutdown -p now") + else() +- set(MINIMUM_VT 7) + set(HALT_COMMAND "/sbin/shutdown -h -P now") + endif() + set(REBOOT_COMMAND "/sbin/shutdown -r now") +diff --git a/data/man/sddm.conf.rst.in b/data/man/sddm.conf.rst.in +index bee07681d..1061540c0 100644 +--- a/data/man/sddm.conf.rst.in ++++ b/data/man/sddm.conf.rst.in +@@ -144,7 +144,7 @@ OPTIONS + Minimum virtual terminal number that will be used + by the first display. Virtual terminal number will + increase as new displays added. +- Default value is @MINIMUM_VT@. ++ This setting is no longer available since SDDM v0.20. + + `EnableHiDPI=` + Enables Qt's automatic HiDPI scaling. +diff --git a/src/common/Configuration.h b/src/common/Configuration.h +index cf44a629c..b79871988 100644 +--- a/src/common/Configuration.h ++++ b/src/common/Configuration.h +@@ -70,7 +70,6 @@ namespace SDDM { + Entry(UserAuthFile, QString, _S(".Xauthority"), _S("Path to the Xauthority file")); + Entry(DisplayCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xsetup"), _S("Path to a script to execute when starting the display server")); + Entry(DisplayStopCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xstop"), _S("Path to a script to execute when stopping the display server")); +- Entry(MinimumVT, int, MINIMUM_VT, _S("The lowest virtual terminal number that will be used.")); + Entry(EnableHiDPI, bool, false, _S("Enable Qt's automatic high-DPI scaling")); + ); + +diff --git a/src/common/Constants.h.in b/src/common/Constants.h.in +index e174b5bfc..480512880 100644 +--- a/src/common/Constants.h.in ++++ b/src/common/Constants.h.in +@@ -37,7 +37,6 @@ + #define SYSTEM_CONFIG_DIR "@SYSTEM_CONFIG_DIR@" + + #define LOG_FILE "@LOG_FILE@" +-#define MINIMUM_VT @MINIMUM_VT@ + + #define UID_MIN @UID_MIN@ + #define UID_MAX @UID_MAX@ +diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp +index ed13bae79..3c77454fc 100644 +--- a/src/daemon/Display.cpp ++++ b/src/daemon/Display.cpp +@@ -46,6 +46,7 @@ + + #include "Login1Manager.h" + #include "Login1Session.h" ++#include "VirtualTerminal.h" + + #if defined(Q_OS_LINUX) + #include <utmp.h> +@@ -53,14 +54,16 @@ + #include <utmpx.h> + + namespace SDDM { +- Display::Display(const int terminalId, Seat *parent) : QObject(parent), +- m_terminalId(terminalId), ++ 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)) { + ++ // Allocate vt ++ m_terminalId = VirtualTerminal::setUpNewVt(); ++ + // respond to authentication requests + m_auth->setVerbose(true); + connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged); +diff --git a/src/daemon/Display.h b/src/daemon/Display.h +index 8b3c2c635..61dd9f630 100644 +--- a/src/daemon/Display.h ++++ b/src/daemon/Display.h +@@ -41,7 +41,7 @@ namespace SDDM { + Q_OBJECT + Q_DISABLE_COPY(Display) + public: +- explicit Display(int terminalId, Seat *parent); ++ explicit Display(Seat *parent); + ~Display(); + + QString displayId() const; +diff --git a/src/daemon/Seat.cpp b/src/daemon/Seat.cpp +index 838c2221d..a2f3d0c39 100644 +--- a/src/daemon/Seat.cpp ++++ b/src/daemon/Seat.cpp +@@ -33,18 +33,6 @@ + #include <functional> + + namespace SDDM { +- int findUnused(int minimum, std::function<bool(const int)> used) { +- // initialize with minimum +- int number = minimum; +- +- // find unused +- while (used(number)) +- number++; +- +- // return number; +- return number; +- } +- + Seat::Seat(const QString &name, QObject *parent) : QObject(parent), m_name(name) { + createDisplay(); + } +@@ -53,30 +41,13 @@ namespace SDDM { + return m_name; + } + +- void Seat::createDisplay(int terminalId) { ++ void Seat::createDisplay() { + //reload config if needed + mainConfig.load(); + +- if (m_name == QLatin1String("seat0")) { +- if (terminalId == -1) { +- // find unused terminal +- terminalId = findUnused(mainConfig.X11.MinimumVT.get(), [&](const int number) { +- return m_terminalIds.contains(number); +- }); +- } +- +- // mark terminal as used +- m_terminalIds << terminalId; +- +- // log message +- qDebug() << "Adding new display" << "on vt" << terminalId << "..."; +- } +- else { +- qDebug() << "Adding new VT-less display..."; +- } +- + // create a new display +- Display *display = new Display(terminalId, this); ++ qDebug() << "Adding new display..."; ++ Display *display = new Display(this); + + // restart display on stop + connect(display, &Display::stopped, this, &Seat::displayStopped); +@@ -112,9 +83,6 @@ namespace SDDM { + // remove display from list + m_displays.removeAll(display); + +- // mark display and terminal ids as unused +- m_terminalIds.removeAll(display->terminalId()); +- + // stop the display + display->blockSignals(true); + display->stop(); +diff --git a/src/daemon/Seat.h b/src/daemon/Seat.h +index f9fe7331f..685eaedd2 100644 +--- a/src/daemon/Seat.h ++++ b/src/daemon/Seat.h +@@ -35,7 +35,7 @@ namespace SDDM { + const QString &name() const; + + public slots: +- void createDisplay(int terminalId = -1); ++ void createDisplay(); + void removeDisplay(SDDM::Display* display); + + private slots: +@@ -47,7 +47,6 @@ namespace SDDM { + QString m_name; + + QVector<Display *> m_displays; +- QVector<int> m_terminalIds; + }; + } + diff --git a/user/sddm/authed.patch b/user/sddm/authed.patch new file mode 100644 index 000000000..fe76652f8 --- /dev/null +++ b/user/sddm/authed.patch @@ -0,0 +1,32 @@ +From b2520c9fdeff081b1ac66a4a15289cedfa6944b9 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Wed, 9 Dec 2020 19:33:08 +0100 +Subject: [PATCH] Emit XorgDisplayServer::started only when the auth file is + ready + +--- + src/daemon/XorgDisplayServer.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp +index 3a7bee0d8..331adcda7 100644 +--- a/src/daemon/XorgDisplayServer.cpp ++++ b/src/daemon/XorgDisplayServer.cpp +@@ -219,8 +219,6 @@ namespace SDDM { + // close our pipe + close(pipeFds[0]); + +- emit started(); +- + // 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")) { +@@ -232,6 +230,8 @@ namespace SDDM { + } + changeOwner(m_authPath); + ++ emit started(); ++ + // set flag + m_started = true; + diff --git a/user/sddm/autologin-type.patch b/user/sddm/autologin-type.patch new file mode 100644 index 000000000..cc5738065 --- /dev/null +++ b/user/sddm/autologin-type.patch @@ -0,0 +1,31 @@ +From e81dfcd6913c4fbd1801597168291b1e396633d8 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Wed, 6 Jan 2021 16:00:34 +0100 +Subject: [PATCH] Fix sessions being started as the wrong type on autologin + +For autologin, the last session is used, which contains a full path. +Display::findSessionEntry didn't handle that correctly, which led to +X11 sessions getting started as Wayland ones (or the other way around +before 994fa67). + +Fixes #1348 +--- + src/daemon/Display.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp +index b95f6e51e..9f1fabc68 100644 +--- a/src/daemon/Display.cpp ++++ b/src/daemon/Display.cpp +@@ -245,6 +245,11 @@ namespace SDDM { + } + + bool Display::findSessionEntry(const QDir &dir, const QString &name) const { ++ // Given an absolute path: Check that it matches dir ++ const QFileInfo fileInfo(name); ++ if (fileInfo.isAbsolute() && fileInfo.absolutePath() != dir.absolutePath()) ++ return false; ++ + QString fileName = name; + + // append extension diff --git a/user/sddm/cleanup-xorg.patch b/user/sddm/cleanup-xorg.patch new file mode 100644 index 000000000..7949b2dbf --- /dev/null +++ b/user/sddm/cleanup-xorg.patch @@ -0,0 +1,63 @@ +From 3b00cd06f82837ba85f37c9f632261e7b4c6fd35 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Wed, 9 Dec 2020 19:28:41 +0100 +Subject: [PATCH] Explicitly stop Xorg when starting fails + +When Xorg starts but there is an error, stop it explicitly instead of assuming +that X exits itself. This avoids a possibly lingering Xorg process in the +XorgDisplayServer instance. Add a check and warning message if Xorg is +restarted too early (shouldn't happen). +--- + src/daemon/XorgDisplayServer.cpp | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp +index 5f40fe8c3..3a7bee0d8 100644 +--- a/src/daemon/XorgDisplayServer.cpp ++++ b/src/daemon/XorgDisplayServer.cpp +@@ -118,6 +118,11 @@ namespace SDDM { + if (m_started) + return false; + ++ if (process) { ++ qCritical() << "Tried to start Xorg before previous instance exited"; ++ return false; ++ } ++ + // create process + process = new QProcess(this); + +@@ -195,6 +200,7 @@ namespace SDDM { + qCritical("Failed to open pipe to start X Server"); + + close(pipeFds[0]); ++ stop(); + return false; + } + QByteArray displayNumber = readPipe.readLine(); +@@ -203,6 +209,7 @@ namespace SDDM { + qCritical("Failed to read display number from pipe"); + + close(pipeFds[0]); ++ stop(); + return false; + } + displayNumber.prepend(QByteArray(":")); +@@ -219,6 +226,7 @@ namespace SDDM { + if(m_display != QStringLiteral(":0")) { + if(!addCookie(m_authPath)) { + qCritical() << "Failed to write xauth file"; ++ stop(); + return false; + } + } +@@ -232,8 +240,7 @@ namespace SDDM { + } + + void XorgDisplayServer::stop() { +- // check flag +- if (!m_started) ++ if (!process) + return; + + // log message diff --git a/user/sddm/qt515.patch b/user/sddm/qt515.patch new file mode 100644 index 000000000..df58ca9fe --- /dev/null +++ b/user/sddm/qt515.patch @@ -0,0 +1,199 @@ +From 3eaa0d6ab1c7efc0bdd91fcd789d8187c6c27678 Mon Sep 17 00:00:00 2001 +From: Aleix Pol <aleixpol@kde.org> +Date: Thu, 24 Jun 2021 12:34:13 +0200 +Subject: [PATCH] Bump to Qt 5.15, port away from deprecated APIs + +--- + CMakeLists.txt | 2 +- + README.md | 2 +- + src/auth/Auth.cpp | 6 +++--- + src/auth/AuthRequest.cpp | 2 +- + src/common/ConfigReader.cpp | 4 ++-- + src/daemon/PowerManager.cpp | 8 ++++++-- + src/daemon/XorgDisplayServer.cpp | 17 +++++++++-------- + src/greeter/GreeterProxy.cpp | 2 +- + 8 files changed, 24 insertions(+), 19 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 41aee21d4..6936b31f6 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -96,7 +96,7 @@ find_package(XCB REQUIRED) + find_package(XKB REQUIRED) + + # Qt 5 +-find_package(Qt5 5.8.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test) ++find_package(Qt5 5.15.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test) + + # find qt5 imports dir + get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION) +diff --git a/README.md b/README.md +index 7c7a55588..97cb2221c 100644 +--- a/README.md ++++ b/README.md +@@ -39,7 +39,7 @@ money that can be used to support the features that you most desire. + + ## INSTALLATION + +-Qt >= 5.8.0 is required to use SDDM. ++Qt >= 5.15.0 is required to use SDDM. + + SDDM runs the greeter as a system user named "sddm" whose home directory needs + to be set to `/var/lib/sddm`. +diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp +index 4450cc606..1dc2dd186 100644 +--- a/src/auth/Auth.cpp ++++ b/src/auth/Auth.cpp +@@ -134,7 +134,7 @@ namespace SDDM { + env.insert(QStringLiteral("LANG"), QStringLiteral("C")); + child->setProcessEnvironment(env); + connect(child, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished), this, &Auth::Private::childExited); +- connect(child, QOverload<QProcess::ProcessError>::of(&QProcess::error), this, &Auth::Private::childError); ++ connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError); + connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished); + connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged); + } +@@ -267,8 +267,8 @@ namespace SDDM { + } + + void Auth::registerTypes() { +- qmlRegisterType<AuthPrompt>(); +- qmlRegisterType<AuthRequest>(); ++ qmlRegisterAnonymousType<AuthPrompt>("Auth", 1); ++ qmlRegisterAnonymousType<AuthRequest>("Auth", 1); + qmlRegisterType<Auth>("Auth", 1, 0, "Auth"); + } + +diff --git a/src/auth/AuthRequest.cpp b/src/auth/AuthRequest.cpp +index 9583b0a4d..9ccc2d259 100644 +--- a/src/auth/AuthRequest.cpp ++++ b/src/auth/AuthRequest.cpp +@@ -73,7 +73,7 @@ namespace SDDM { + } + + QQmlListProperty<AuthPrompt> AuthRequest::promptsDecl() { +- return QQmlListProperty<AuthPrompt>(this, d->prompts); ++ return QQmlListProperty<AuthPrompt>(this, &d->prompts); + } + + void AuthRequest::done() { +diff --git a/src/common/ConfigReader.cpp b/src/common/ConfigReader.cpp +index 041e5ed22..503b55e69 100644 +--- a/src/common/ConfigReader.cpp ++++ b/src/common/ConfigReader.cpp +@@ -265,7 +265,7 @@ namespace SDDM { + QByteArray junk; + // stores the junk to the temporary storage + auto collectJunk = [&junk](const QString &data) { +- junk.append(data); ++ junk.append(data.toUtf8()); + }; + + // a short function to assign the current junk and current line to the right section, eventually create a new one +@@ -275,7 +275,7 @@ namespace SDDM { + sectionData[currentSection] = QByteArray(); + } + sectionData[currentSection].append(junk); +- sectionData[currentSection].append(data); ++ sectionData[currentSection].append(data.toUtf8()); + junk.clear(); + }; + +diff --git a/src/daemon/PowerManager.cpp b/src/daemon/PowerManager.cpp +index 6eca29f55..ab1d6ac03 100644 +--- a/src/daemon/PowerManager.cpp ++++ b/src/daemon/PowerManager.cpp +@@ -87,11 +87,15 @@ const QString UPOWER_OBJECT = QStringLiteral("org.freedesktop.UPower"); + } + + void powerOff() const { +- QProcess::execute(mainConfig.HaltCommand.get()); ++ auto command = QProcess::splitCommand(mainConfig.HaltCommand.get()); ++ const QString program = command.takeFirst(); ++ QProcess::execute(program, command); + } + + void reboot() const { +- QProcess::execute(mainConfig.RebootCommand.get()); ++ auto command = QProcess::splitCommand(mainConfig.RebootCommand.get()); ++ const QString program = command.takeFirst(); ++ QProcess::execute(program, command); + } + + void suspend() const { +diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp +index fc61ee2dd..13a1691b8 100644 +--- a/src/daemon/XorgDisplayServer.cpp ++++ b/src/daemon/XorgDisplayServer.cpp +@@ -110,7 +110,7 @@ namespace SDDM { + QStringList args; + if (!daemonApp->testing()) { + process->setProgram(mainConfig.X11.ServerPath.get()); +- args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts) ++ args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) + << QStringLiteral("-background") << QStringLiteral("none") + << QStringLiteral("-seat") << displayPtr()->seat()->name() + << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); +@@ -221,7 +221,7 @@ namespace SDDM { + // log message + qDebug() << "Display server stopped."; + +- QString displayStopCommand = mainConfig.X11.DisplayStopCommand.get(); ++ QStringList displayStopCommand = QProcess::splitCommand(mainConfig.X11.DisplayStopCommand.get()); + + // create display setup script process + QProcess *displayStopScript = new QProcess(); +@@ -236,7 +236,8 @@ namespace SDDM { + + // start display stop script + qDebug() << "Running display stop script " << displayStopCommand; +- displayStopScript->start(displayStopCommand); ++ const auto program = displayStopCommand.takeFirst(); ++ displayStopScript->start(program, displayStopCommand); + + // wait for finished + if (!displayStopScript->waitForFinished(5000)) +@@ -254,8 +255,6 @@ namespace SDDM { + } + + void XorgDisplayServer::setupDisplay() { +- QString displayCommand = mainConfig.X11.DisplayCommand.get(); +- + // create cursor setup process + QProcess *setCursor = new QProcess(); + // create display setup script process +@@ -273,7 +272,7 @@ namespace SDDM { + displayScript->setProcessEnvironment(env); + + qDebug() << "Setting default cursor"; +- setCursor->start(QStringLiteral("xsetroot -cursor_name left_ptr")); ++ setCursor->start(QStringLiteral("xsetroot"), { QStringLiteral("-cursor_name"), QStringLiteral("left_ptr") }); + + // delete setCursor on finish + connect(setCursor, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), setCursor, &QProcess::deleteLater); +@@ -285,8 +284,10 @@ namespace SDDM { + } + + // start display setup script +- qDebug() << "Running display setup script " << displayCommand; +- displayScript->start(displayCommand); ++ qDebug() << "Running display setup script " << mainConfig.X11.DisplayCommand.get(); ++ QStringList displayCommand = QProcess::splitCommand(mainConfig.X11.DisplayCommand.get()); ++ const QString program = displayCommand.takeFirst(); ++ displayScript->start(program, displayCommand); + + // delete displayScript on finish + connect(displayScript, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), displayScript, &QProcess::deleteLater); +diff --git a/src/greeter/GreeterProxy.cpp b/src/greeter/GreeterProxy.cpp +index 8fde3d904..d6e62cc75 100644 +--- a/src/greeter/GreeterProxy.cpp ++++ b/src/greeter/GreeterProxy.cpp +@@ -46,7 +46,7 @@ namespace SDDM { + connect(d->socket, &QLocalSocket::connected, this, &GreeterProxy::connected); + connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected); + connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead); +- connect(d->socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &GreeterProxy::error); ++ connect(d->socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error); + + // connect to server + d->socket->connectToServer(socket); diff --git a/user/sddm/retry.patch b/user/sddm/retry.patch new file mode 100644 index 000000000..84edc7f08 --- /dev/null +++ b/user/sddm/retry.patch @@ -0,0 +1,115 @@ +From 42c51761cc82edbaa50d702a4614e179ad4bcd63 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Thu, 12 Nov 2020 20:30:55 +0100 +Subject: [PATCH] Retry starting the display server + +Even if the CanGraphical property of a Seat is true, it's possible that it's +still too early for X to start, as it might need some driver or device which +isn't present yet. + +Fixes #1316 +--- + src/daemon/Seat.cpp | 23 ++++++++++++++++++----- + src/daemon/Seat.h | 4 +++- + src/daemon/XorgDisplayServer.cpp | 10 ++++++---- + 3 files changed, 27 insertions(+), 10 deletions(-) + +diff --git a/src/daemon/Seat.cpp b/src/daemon/Seat.cpp +index eef26da45..838c2221d 100644 +--- a/src/daemon/Seat.cpp ++++ b/src/daemon/Seat.cpp +@@ -28,6 +28,7 @@ + + #include <QDebug> + #include <QFile> ++#include <QTimer> + + #include <functional> + +@@ -52,7 +53,7 @@ namespace SDDM { + return m_name; + } + +- bool Seat::createDisplay(int terminalId) { ++ void Seat::createDisplay(int terminalId) { + //reload config if needed + mainConfig.load(); + +@@ -84,12 +85,24 @@ namespace SDDM { + m_displays << display; + + // start the display +- if (!display->start()) { +- qCritical() << "Could not start Display server on vt" << terminalId; +- return false; ++ startDisplay(display); ++ } ++ ++ void Seat::startDisplay(Display *display, int tryNr) { ++ if (display->start()) ++ return; ++ ++ // It's possible that the system isn't ready yet (driver not loaded, ++ // device not enumerated, ...). It's not possible to tell when that changes, ++ // so try a few times with a delay in between. ++ qWarning() << "Attempt" << tryNr << "starting the Display server on vt" << display->terminalId() << "failed"; ++ ++ if(tryNr >= 3) { ++ qCritical() << "Could not start Display server on vt" << display->terminalId(); ++ return; + } + +- return true; ++ QTimer::singleShot(2000, display, [=] { startDisplay(display, tryNr + 1); }); + } + + void Seat::removeDisplay(Display* display) { +diff --git a/src/daemon/Seat.h b/src/daemon/Seat.h +index bf22566b7..f9fe7331f 100644 +--- a/src/daemon/Seat.h ++++ b/src/daemon/Seat.h +@@ -35,13 +35,15 @@ namespace SDDM { + const QString &name() const; + + public slots: +- bool createDisplay(int terminalId = -1); ++ void createDisplay(int terminalId = -1); + void removeDisplay(SDDM::Display* display); + + private slots: + void displayStopped(); + + private: ++ void startDisplay(SDDM::Display *display, int tryNr = 1); ++ + QString m_name; + + QVector<Display *> m_displays; +diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp +index e60c02210..5f40fe8c3 100644 +--- a/src/daemon/XorgDisplayServer.cpp ++++ b/src/daemon/XorgDisplayServer.cpp +@@ -248,6 +248,12 @@ namespace SDDM { + } + + void XorgDisplayServer::finished() { ++ // clean up ++ if (process) { ++ process->deleteLater(); ++ process = nullptr; ++ } ++ + // check flag + if (!m_started) + return; +@@ -283,10 +289,6 @@ namespace SDDM { + displayStopScript->deleteLater(); + displayStopScript = nullptr; + +- // clean up +- process->deleteLater(); +- process = nullptr; +- + // remove authority file + QFile::remove(m_authPath); + diff --git a/user/sddm/revert-fedora.patch b/user/sddm/revert-fedora.patch new file mode 100644 index 000000000..63bb58395 --- /dev/null +++ b/user/sddm/revert-fedora.patch @@ -0,0 +1,74 @@ +From 994fa67b01ccfac1aaac08572302bbbea7842dc3 Mon Sep 17 00:00:00 2001 +Subject: Revert: Prefer Wayland sessions over X11 ones + +Reference: https://fedoraproject.org/wiki/Changes/WaylandByDefaultForPlasma +--- + src/common/Session.cpp | 8 ++++---- + src/daemon/Display.cpp | 6 +++--- + src/greeter/SessionModel.cpp | 6 +++--- + 3 files changed, 10 insertions(+), 10 deletions(-) + +--- b/src/common/Session.cpp ++++ a/src/common/Session.cpp +@@ -131,14 +131,14 @@ + m_desktopNames.clear(); + + switch (type) { ++ case X11Session: ++ m_dir = QDir(mainConfig.X11.SessionDir.get()); ++ m_xdgSessionType = QStringLiteral("x11"); ++ break; + case WaylandSession: + m_dir = QDir(mainConfig.Wayland.SessionDir.get()); + m_xdgSessionType = QStringLiteral("wayland"); + break; +- case X11Session: +- m_dir = QDir(mainConfig.X11.SessionDir.get()); +- m_xdgSessionType = QStringLiteral("x11"); +- break; + default: + m_xdgSessionType.clear(); + break; +--- b/src/daemon/Display.cpp ++++ a/src/daemon/Display.cpp +@@ -115,10 +115,10 @@ + if (autologinSession.isEmpty()) { + autologinSession = stateConfig.Last.Session.get(); + } ++ if (findSessionEntry(mainConfig.X11.SessionDir.get(), autologinSession)) { ++ sessionType = Session::X11Session; ++ } else if (findSessionEntry(mainConfig.Wayland.SessionDir.get(), autologinSession)) { +- if (findSessionEntry(mainConfig.Wayland.SessionDir.get(), autologinSession)) { + sessionType = Session::WaylandSession; +- } else if (findSessionEntry(mainConfig.X11.SessionDir.get(), autologinSession)) { +- sessionType = Session::X11Session; + } else { + qCritical() << "Unable to find autologin session entry" << autologinSession; + return false; +--- b/src/greeter/SessionModel.cpp ++++ a/src/greeter/SessionModel.cpp +@@ -41,8 +41,8 @@ + SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) { + // initial population + beginResetModel(); ++ populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); +- populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + endResetModel(); + + // refresh everytime a file is changed, added or removed +@@ -50,12 +50,12 @@ + connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) { + beginResetModel(); + d->sessions.clear(); ++ populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); +- populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + endResetModel(); + }); ++ watcher->addPath(mainConfig.X11.SessionDir.get()); + watcher->addPath(mainConfig.Wayland.SessionDir.get()); +- watcher->addPath(mainConfig.X11.SessionDir.get()); + } + + SessionModel::~SessionModel() { 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 diff --git a/user/sddm/session-env.patch b/user/sddm/session-env.patch new file mode 100644 index 000000000..e73ab8fc4 --- /dev/null +++ b/user/sddm/session-env.patch @@ -0,0 +1,122 @@ +From 093e8e54c7de33c2d7332cc3a126041263e1aa71 Mon Sep 17 00:00:00 2001 +From: davidedmundson <kde@davidedmundson.co.uk> +Date: Mon, 8 Mar 2021 07:35:55 +0000 +Subject: [PATCH] Allow addition env vars to be defined in session files + (#1370) + +We have a continual problem with developers using Plasma in custom +prefixes. We need to set mulitple additional envs and it is a constant +pain point. + +Setting XDG_DATA_DIRS and alike in a script in the Exec line of the +desktop file doesn't work properly, as this is after systemd and dbus +have already been started during pam_logind. It is currently very bodged +with more manual steps. + +Things will work much better if we can set the env before pam_logind is +run. + +Using pam_env or profile.d doesn't work as that affects all the +sessions. + +It is not intended to be used for anything other than dev setups. +--- + src/common/Session.cpp | 24 ++++++++++++++++++++++++ + src/common/Session.h | 5 +++++ + src/daemon/Display.cpp | 1 + + 3 files changed, 30 insertions(+) + +diff --git a/src/common/Session.cpp b/src/common/Session.cpp +index 3de28ef13..0aa540221 100644 +--- a/src/common/Session.cpp ++++ b/src/common/Session.cpp +@@ -107,6 +107,10 @@ namespace SDDM { + return m_isNoDisplay; + } + ++ QProcessEnvironment Session::additionalEnv() const { ++ return m_additionalEnv; ++ } ++ + void Session::setTo(Type type, const QString &_fileName) + { + QString fileName(_fileName); +@@ -178,6 +182,8 @@ namespace SDDM { + m_isHidden = line.mid(7).toLower() == QLatin1String("true"); + if (line.startsWith(QLatin1String("NoDisplay="))) + m_isNoDisplay = line.mid(10).toLower() == QLatin1String("true"); ++ if (line.startsWith(QLatin1String("X-SDDM-Env="))) ++ m_additionalEnv = parseEnv(line.mid(strlen("X-SDDM-Env="))); + } + + file.close(); +@@ -191,4 +197,22 @@ namespace SDDM { + setTo(other.type(), other.fileName()); + return *this; + } ++ ++ QProcessEnvironment SDDM::Session::parseEnv(const QString &list) ++ { ++ QProcessEnvironment env; ++ ++ const QVector<QStringRef> entryList = list.splitRef(QLatin1Char(',')); ++ for (const auto &entry: entryList) { ++ int midPoint = entry.indexOf(QLatin1Char('=')); ++ if (midPoint < 0) { ++ qWarning() << "Malformed entry in" << fileName() << ":" << entry; ++ continue; ++ } ++ env.insert(entry.left(midPoint).toString(), entry.mid(midPoint+1).toString()); ++ } ++ return env; ++ } ++ ++ + } +diff --git a/src/common/Session.h b/src/common/Session.h +index 9fd86cd02..aa196e9c6 100644 +--- a/src/common/Session.h ++++ b/src/common/Session.h +@@ -23,6 +23,7 @@ + #include <QDataStream> + #include <QDir> + #include <QSharedPointer> ++#include <QProcessEnvironment> + + namespace SDDM { + class SessionModel; +@@ -59,11 +60,14 @@ namespace SDDM { + bool isHidden() const; + bool isNoDisplay() const; + ++ QProcessEnvironment additionalEnv() const; ++ + void setTo(Type type, const QString &name); + + Session &operator=(const Session &other); + + private: ++ QProcessEnvironment parseEnv(const QString &list); + bool m_valid; + Type m_type; + QDir m_dir; +@@ -75,6 +79,7 @@ namespace SDDM { + QString m_tryExec; + QString m_xdgSessionType; + QString m_desktopNames; ++ QProcessEnvironment m_additionalEnv; + bool m_isHidden; + bool m_isNoDisplay; + +diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp +index 9f1fabc68..a65df3f0b 100644 +--- a/src/daemon/Display.cpp ++++ b/src/daemon/Display.cpp +@@ -313,6 +313,7 @@ namespace SDDM { + 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 diff --git a/user/sddm/utmpx.patch b/user/sddm/utmpx.patch index 1edd72d7d..4402fd861 100644 --- a/user/sddm/utmpx.patch +++ b/user/sddm/utmpx.patch @@ -1,10 +1,12 @@ ---- sddm-0.18.0/src/helper/HelperApp.cpp.old 2018-07-18 10:31:40.000000000 +0000 -+++ sddm-0.18.0/src/helper/HelperApp.cpp 2018-07-25 23:11:09.840000000 +0000 -@@ -35,7 +35,6 @@ +--- sddm-0.19.0/src/helper/HelperApp.cpp.old 2020-11-03 03:47:51.000000000 -0600 ++++ sddm-0.19.0/src/helper/HelperApp.cpp 2022-08-19 21:59:19.977071884 -0500 +@@ -36,9 +36,6 @@ #include <sys/socket.h> #include <sys/time.h> +-#if defined(Q_OS_LINUX) -#include <utmp.h> +-#endif #include <utmpx.h> #include <QByteArray> diff --git a/user/sddm/xephyr.patch b/user/sddm/xephyr.patch new file mode 100644 index 000000000..142520ad4 --- /dev/null +++ b/user/sddm/xephyr.patch @@ -0,0 +1,171 @@ +From 68cc9e31d1a4c4609f42114782fc485cb07353a4 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Fri, 9 Oct 2020 21:06:01 +0200 +Subject: [PATCH] Merge normal and testing paths in XorgDisplayServer::start + +They have much in common and this means that Xephyr can also make use use +of -displayfd now. +--- + src/daemon/XorgDisplayServer.cpp | 132 ++++++++++++++----------------- + 1 file changed, 60 insertions(+), 72 deletions(-) + +diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp +index d5f29a94a..e60c02210 100644 +--- a/src/daemon/XorgDisplayServer.cpp ++++ b/src/daemon/XorgDisplayServer.cpp +@@ -136,95 +136,83 @@ namespace SDDM { + return false; + } + +- if (daemonApp->testing()) { +- QStringList args; +- QDir x11socketDir(QStringLiteral("/tmp/.X11-unix")); +- int display = 100; +- while (x11socketDir.exists(QStringLiteral("X%1").arg(display))) { +- ++display; +- } +- m_display = QStringLiteral(":%1").arg(display); +- args << m_display << QStringLiteral("-auth") << m_authPath << QStringLiteral("-br") << QStringLiteral("-noreset") << QStringLiteral("-screen") << QStringLiteral("800x600"); +- process->start(mainConfig.X11.XephyrPath.get(), args); +- +- +- // wait for display server to start +- if (!process->waitForStarted()) { +- // log message +- qCritical() << "Failed to start display server process."; ++ // set process environment ++ QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); ++ env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); ++ process->setProcessEnvironment(env); + +- // return fail +- return false; +- } +- emit started(); +- } else { +- // set process environment +- QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); +- env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); +- process->setProcessEnvironment(env); +- +- //create pipe for communicating with X server +- //0 == read from X, 1== write to from X +- int pipeFds[2]; +- if (pipe(pipeFds) != 0) { +- qCritical("Could not create pipe to start X server"); +- } ++ //create pipe for communicating with X server ++ //0 == read from X, 1== write to from X ++ int pipeFds[2]; ++ if (pipe(pipeFds) != 0) { ++ qCritical("Could not create pipe to start X server"); ++ } + +- // start display server +- QStringList args = mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts); +- args << QStringLiteral("-auth") << m_authPath ++ // start display server ++ QStringList args; ++ if (!daemonApp->testing()) { ++ process->setProgram(mainConfig.X11.ServerPath.get()); ++ args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts) + << QStringLiteral("-background") << QStringLiteral("none") +- << QStringLiteral("-noreset") +- << QStringLiteral("-displayfd") << QString::number(pipeFds[1]) + << QStringLiteral("-seat") << displayPtr()->seat()->name(); + + if (displayPtr()->seat()->name() == QLatin1String("seat0")) { + args << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); + } +- qDebug() << "Running:" +- << qPrintable(mainConfig.X11.ServerPath.get()) +- << qPrintable(args.join(QLatin1Char(' '))); +- process->start(mainConfig.X11.ServerPath.get(), args); +- +- // wait for display server to start +- if (!process->waitForStarted()) { +- // log message +- qCritical() << "Failed to start display server process."; +- +- // return fail +- close(pipeFds[0]); +- return false; +- } ++ } else { ++ process->setProgram(mainConfig.X11.XephyrPath.get()); ++ args << QStringLiteral("-br") ++ << QStringLiteral("-screen") << QStringLiteral("800x600"); ++ } + +- // close the other side of pipe in our process, otherwise reading +- // from it may stuck even X server exit. +- close(pipeFds[1]); ++ args << QStringLiteral("-auth") << m_authPath ++ << QStringLiteral("-noreset") ++ << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + +- QFile readPipe; ++ process->setArguments(args); ++ qDebug() << "Running:" ++ << qPrintable(process->program()) ++ << qPrintable(process->arguments().join(QLatin1Char(' '))); ++ process->start(); + +- if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { +- qCritical("Failed to open pipe to start X Server"); ++ // wait for display server to start ++ if (!process->waitForStarted()) { ++ // log message ++ qCritical() << "Failed to start display server process."; + +- 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"); ++ // return fail ++ close(pipeFds[0]); ++ return false; ++ } + +- close(pipeFds[0]); +- return false; +- } +- displayNumber.prepend(QByteArray(":")); +- displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace +- m_display = QString::fromLocal8Bit(displayNumber); ++ // close the other side of pipe in our process, otherwise reading ++ // from it may stuck even X server exit. ++ close(pipeFds[1]); ++ ++ QFile readPipe; ++ ++ if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { ++ qCritical("Failed to open pipe to start X Server"); + +- // close our pipe + 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"); + +- emit started(); ++ close(pipeFds[0]); ++ return false; + } ++ displayNumber.prepend(QByteArray(":")); ++ displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace ++ m_display = QString::fromLocal8Bit(displayNumber); ++ ++ // close our pipe ++ close(pipeFds[0]); ++ ++ emit started(); + + // The file is also used by the greeter, which does care about the + // display number. Write the proper entry, if it's different. |