From f541d851edd9741c44bfc98fb29ed21a5f82d153 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 28 Jul 2021 11:03:10 +0100 Subject: [PATCH] [kwin_wrapper] Transfer ownership of Xwayland socket creation and Xauthority to the wrapper This way if kwin_wayland crashes we don't need to ensure that new environment variables need to get synced across the new env. This fixes an issue where spawning an xwayland application from a wayland window that survives a crash would fail. By moving the logic here we no longer need to wait for kwin_wayland to start before starting plasmashell or even ksmserver as all environment variables are set. As long as the wrapper is ready we can continue starting and clients will just block on connect. That should still allow for both a lot of optimisations both for speed and cleaning up the startplasma-wayland scripts. This will be addressed in follow up patches. Use of kwin_wayland directly with xwayland is still supported for testing. --- src/CMakeLists.txt | 27 +-- src/helpers/wayland_wrapper/CMakeLists.txt | 21 ++- src/helpers/wayland_wrapper/kwin_wrapper.cpp | 42 ++++- src/main_wayland.cpp | 46 ++++- src/main_wayland.h | 12 ++ src/xwl/CMakeLists.txt | 15 ++ src/xwl/lib/CMakeLists.txt | 19 ++ src/xwl/lib/xauthority.cpp | 77 ++++++++ src/xwl/lib/xauthority.h | 14 ++ src/xwl/{ => lib}/xwaylandsocket.cpp | 14 +- src/xwl/{ => lib}/xwaylandsocket.h | 7 +- src/xwl/xwayland.cpp | 184 ++++++------------- src/xwl/xwayland.h | 28 ++- 13 files changed, 338 insertions(+), 168 deletions(-) create mode 100644 src/xwl/CMakeLists.txt create mode 100644 src/xwl/lib/CMakeLists.txt create mode 100644 src/xwl/lib/xauthority.cpp create mode 100644 src/xwl/lib/xauthority.h rename src/xwl/{ => lib}/xwaylandsocket.cpp (93%) rename src/xwl/{ => lib}/xwaylandsocket.h (81%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3fa9c8d7b2..47e4db639f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -270,32 +270,7 @@ target_link_libraries(kwin_x11 kwin KF5::Crash Qt::X11Extras) install(TARGETS kwin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) install(TARGETS kwin_x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -ecm_qt_declare_logging_category(xwaylandlogging_SOURCES - HEADER - xwayland_logging.h - IDENTIFIER - KWIN_XWL - CATEGORY_NAME - kwin_xwl - DEFAULT_SEVERITY - Warning -) -add_library(KWinXwaylandServerModule OBJECT - xwl/clipboard.cpp - xwl/databridge.cpp - xwl/dnd.cpp - xwl/drag.cpp - xwl/drag_wl.cpp - xwl/drag_x.cpp - xwl/selection.cpp - xwl/selection_source.cpp - xwl/transfer.cpp - xwl/xwayland.cpp - xwl/xwaylandsocket.cpp - - ${xwaylandlogging_SOURCES} -) -target_link_libraries(KWinXwaylandServerModule PUBLIC kwin) +add_subdirectory(xwl) add_executable(kwin_wayland main_wayland.cpp diff --git a/src/helpers/wayland_wrapper/CMakeLists.txt b/src/helpers/wayland_wrapper/CMakeLists.txt index 0627c33ae1..09aae2d8f3 100644 --- a/src/helpers/wayland_wrapper/CMakeLists.txt +++ b/src/helpers/wayland_wrapper/CMakeLists.txt @@ -1,4 +1,21 @@ -add_executable(kwin_wayland_wrapper kwin_wrapper.cpp wl-socket.c) -target_link_libraries(kwin_wayland_wrapper Qt5::Core) +set(kwin_wayland_wrapper_SOURCES + kwin_wrapper.cpp + wl-socket.c +) + +ecm_qt_declare_logging_category(kwin_wayland_wrapper_SOURCES + HEADER + wrapper_logging.h + IDENTIFIER + KWIN_WRAPPER + CATEGORY_NAME + kwin_wayland_wrapper + DEFAULT_SEVERITY + Warning +) + +add_executable(kwin_wayland_wrapper ${kwin_wayland_wrapper_SOURCES}) + +target_link_libraries(kwin_wayland_wrapper Qt5::Core KWinXwaylandCommon) set_property(TARGET kwin_wayland_wrapper PROPERTY C_STANDARD 11) install(TARGETS kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/helpers/wayland_wrapper/kwin_wrapper.cpp b/src/helpers/wayland_wrapper/kwin_wrapper.cpp index dc29455666..f79701c0bf 100644 --- a/src/helpers/wayland_wrapper/kwin_wrapper.cpp +++ b/src/helpers/wayland_wrapper/kwin_wrapper.cpp @@ -24,7 +24,12 @@ #include #include +#include + #include "wl-socket.h" +#include "xwaylandsocket.h" +#include "xauthority.h" +#include "wrapper_logging.h" class KWinWrapper : public QObject { @@ -37,6 +42,9 @@ public: private: wl_socket *m_socket; + + QScopedPointer m_xwlSocket; + QTemporaryFile m_xauthorityFile; }; KWinWrapper::KWinWrapper(QObject *parent) @@ -46,6 +54,21 @@ KWinWrapper::KWinWrapper(QObject *parent) if (!m_socket) { qFatal("Could not create wayland socket"); } + + if (qApp->arguments().contains(QLatin1String("--xwayland"))) { + m_xwlSocket.reset(new KWin::XwaylandSocket(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec)); + if (!m_xwlSocket->isValid()) { + qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets"; + m_xwlSocket.reset(); + } + if (m_xwlSocket) { + if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) { + if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) { + qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file"; + } + } + } + } } KWinWrapper::~KWinWrapper() @@ -66,12 +89,12 @@ void KWinWrapper::run() if (exitStatus == 133) { crashCount = 1; - qDebug() << "Compositor restarted, respawning"; + qCDebug(KWIN_WRAPPER) << "Compositor restarted, respawning"; } else if (exitStatus == -1) { // kwin_crashed, lets go again - qWarning() << "Compositor crashed, respawning"; + qWarning(KWIN_WRAPPER) << "Compositor crashed, respawning"; } else { - qWarning() << "Compositor exited with code: " << exitStatus; + qWarning(KWIN_WRAPPER) << "Compositor exited with code: " << exitStatus; break; } } @@ -79,15 +102,26 @@ void KWinWrapper::run() int KWinWrapper::runKwin() { - qDebug() << "Launching kwin"; + qCDebug(KWIN_WRAPPER) << "Launching kwin"; auto process = new QProcess(qApp); process->setProgram("kwin_wayland"); QStringList args; + args << "--wayland_fd" << QString::number(wl_socket_get_fd(m_socket)); args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket)); + if (m_xwlSocket) { + args << "--xwayland-fd" << QString::number(m_xwlSocket->abstractFileDescriptor()); + args << "--xwayland-fd" << QString::number(m_xwlSocket->unixFileDescriptor()); + args << "--xwayland-display" << m_xwlSocket->name(); + if (m_xauthorityFile.open()) { + args << "--xwayland-xauthority" << m_xauthorityFile.fileName(); + } + + } + // attach our main process arguments // the first entry is dropped as it will be our program name args << qApp->arguments().mid(1); diff --git a/src/main_wayland.cpp b/src/main_wayland.cpp index 6c3d91524d..5a5ca2e532 100644 --- a/src/main_wayland.cpp +++ b/src/main_wayland.cpp @@ -198,6 +198,9 @@ void ApplicationWayland::continueStartupWithScreens() } m_xwayland = new Xwl::Xwayland(this); + m_xwayland->setListenFDs(m_xwaylandListenFds); + m_xwayland->setDisplayName(m_xwaylandDisplay); + m_xwayland->setXauthority(m_xwaylandXauthority); connect(m_xwayland, &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup); connect(m_xwayland, &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup); m_xwayland->start(); @@ -469,6 +472,18 @@ int main(int argc, char * argv[]) i18n("Wayland socket to use for incoming connections. This can be combined with --socket to name the socket"), QStringLiteral("wayland_fd")); + QCommandLineOption xwaylandListenFdOption(QStringLiteral("xwayland-fd"), + i18n("XWayland socket to use for Xwayland's incoming connections. This can be set multiple times"), + QStringLiteral("xwayland-fds")); + + QCommandLineOption xwaylandDisplayOption(QStringLiteral("xwayland-display"), + i18n("Name of the xwayland display that has been pre-set up"), + "xwayland-display"); + + QCommandLineOption xwaylandXAuthorityOption(QStringLiteral("xwayland-xauthority"), + i18n("Name of the xauthority file "), + "xwayland-xauthority"); + QCommandLineOption replaceOption(QStringLiteral("replace"), i18n("Exits this instance so it can be restarted by kwin_wayland_wrapper.")); @@ -477,6 +492,9 @@ int main(int argc, char * argv[]) parser.addOption(xwaylandOption); parser.addOption(waylandSocketOption); parser.addOption(waylandSocketFdOption); + parser.addOption(xwaylandListenFdOption); + parser.addOption(xwaylandDisplayOption); + parser.addOption(xwaylandXAuthorityOption); parser.addOption(replaceOption); if (hasX11Option) { @@ -701,7 +719,33 @@ int main(int argc, char * argv[]) environment.insert(QStringLiteral("WAYLAND_DISPLAY"), server->socketName()); } a.setProcessStartupEnvironment(environment); - a.setStartXwayland(parser.isSet(xwaylandOption)); + + if (parser.isSet(xwaylandOption)) { + a.setStartXwayland(true); + + if (parser.isSet(xwaylandListenFdOption)) { + const QStringList fdStrings = parser.values(xwaylandListenFdOption); + for (const QString &fdString: fdStrings){ + bool ok; + int fd = fdString.toInt(&ok); + if (ok ) { + // make sure we don't leak this FD to children + fcntl(fd, F_SETFD, O_CLOEXEC); + a.addXwaylandSocketFileDescriptor(fd); + } + } + if (parser.isSet(xwaylandDisplayOption)) { + a.setXwaylandDisplay(parser.value(xwaylandDisplayOption)); + } else { + std::cerr << "Using xwayland-fd without xwayland-display is undefined" << std::endl; + return 1; + } + if (parser.isSet(xwaylandXAuthorityOption)) { + a.setXwaylandXauthority(parser.value(xwaylandXAuthorityOption)); + } + } + } + a.setApplicationsToStart(parser.positionalArguments()); a.setInputMethodServerToStart(parser.value(inputMethodOption)); a.start(); diff --git a/src/main_wayland.h b/src/main_wayland.h index 06ba7a42bf..bfbcfc1093 100644 --- a/src/main_wayland.h +++ b/src/main_wayland.h @@ -30,6 +30,15 @@ public: void setStartXwayland(bool start) { m_startXWayland = start; } + void addXwaylandSocketFileDescriptor(int fd) { + m_xwaylandListenFds << fd; + } + void setXwaylandDisplay(const QString &display) { + m_xwaylandDisplay = display; + } + void setXwaylandXauthority(const QString &xauthority) { + m_xwaylandXauthority= xauthority; + } void setApplicationsToStart(const QStringList &applications) { m_applicationsToStart = applications; } @@ -64,6 +73,9 @@ private: QString m_sessionArgument; Xwl::Xwayland *m_xwayland = nullptr; + QVector m_xwaylandListenFds; + QString m_xwaylandDisplay; + QString m_xwaylandXauthority; KConfigWatcher::Ptr m_settingsWatcher; }; diff --git a/src/xwl/CMakeLists.txt b/src/xwl/CMakeLists.txt new file mode 100644 index 0000000000..085c9f484e --- /dev/null +++ b/src/xwl/CMakeLists.txt @@ -0,0 +1,15 @@ +add_subdirectory(lib) + +add_library(KWinXwaylandServerModule OBJECT + clipboard.cpp + databridge.cpp + dnd.cpp + drag.cpp + drag_wl.cpp + drag_x.cpp + selection.cpp + selection_source.cpp + transfer.cpp + xwayland.cpp +) +target_link_libraries(KWinXwaylandServerModule PUBLIC kwin KWinXwaylandCommon) diff --git a/src/xwl/lib/CMakeLists.txt b/src/xwl/lib/CMakeLists.txt new file mode 100644 index 0000000000..94d47cc2c2 --- /dev/null +++ b/src/xwl/lib/CMakeLists.txt @@ -0,0 +1,19 @@ +ecm_qt_declare_logging_category(xwaylandliblogging_SOURCES + HEADER + xwayland_logging.h + IDENTIFIER + KWIN_XWL + CATEGORY_NAME + kwin_xwl + DEFAULT_SEVERITY + Warning +) + +add_library(KWinXwaylandCommon STATIC + xwaylandsocket.cpp + xauthority.cpp + ${xwaylandliblogging_SOURCES} +) + +target_include_directories(KWinXwaylandCommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(KWinXwaylandCommon Qt::Core Qt::Network) diff --git a/src/xwl/lib/xauthority.cpp b/src/xwl/lib/xauthority.cpp new file mode 100644 index 0000000000..a7b61843fd --- /dev/null +++ b/src/xwl/lib/xauthority.cpp @@ -0,0 +1,77 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "xauthority.h" + +#include +#include +#include +#include +#include + +static void writeXauthorityEntry(QDataStream &stream, quint16 family, + const QByteArray &address, const QByteArray &display, + const QByteArray &name, const QByteArray &cookie) +{ + stream << quint16(family); + + auto writeArray = [&stream](const QByteArray &str) { + stream << quint16(str.size()); + stream.writeRawData(str.constData(), str.size()); + }; + + writeArray(address); + writeArray(display); + writeArray(name); + writeArray(cookie); +} + +static QByteArray generateXauthorityCookie() +{ + QByteArray cookie; + cookie.resize(16); // Cookie must be 128bits + + QRandomGenerator *generator = QRandomGenerator::system(); + for (int i = 0; i < cookie.size(); ++i) { + cookie[i] = uint8_t(generator->bounded(256)); + } + return cookie; +} + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile) +{ + const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + + authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX")); + if (!authorityFile->open()) { + return false; + } + + const QByteArray hostname = QHostInfo::localHostName().toUtf8(); + const QByteArray displayName = QByteArray::number(display); + const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); + const QByteArray cookie = generateXauthorityCookie(); + + QDataStream stream(authorityFile); + stream.setByteOrder(QDataStream::BigEndian); + + // Write entry with FamilyLocal and the host name as address + writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie); + + // Write entry with FamilyWild, no address + writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie); + + if (stream.status() != QDataStream::Ok || !authorityFile->flush()) { + authorityFile->remove(); + return false; + } + + return true; +} diff --git a/src/xwl/lib/xauthority.h b/src/xwl/lib/xauthority.h new file mode 100644 index 0000000000..7ba232be09 --- /dev/null +++ b/src/xwl/lib/xauthority.h @@ -0,0 +1,14 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +class QTemporaryFile; + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile); diff --git a/src/xwl/xwaylandsocket.cpp b/src/xwl/lib/xwaylandsocket.cpp similarity index 93% rename from src/xwl/xwaylandsocket.cpp rename to src/xwl/lib/xwaylandsocket.cpp index cae6c22554..786ab5cbbf 100644 --- a/src/xwl/xwaylandsocket.cpp +++ b/src/xwl/lib/xwaylandsocket.cpp @@ -106,11 +106,15 @@ static bool tryLockFile(const QString &lockFileName) return false; } -static int listen_helper(const QString &filePath, UnixSocketAddress::Type type) +static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode) { const UnixSocketAddress socketAddress(filePath, type); - int fileDescriptor = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + int socketFlags = SOCK_STREAM; + if (mode == XwaylandSocket::OperationMode::CloseFdsOnExec) { + socketFlags |= SOCK_CLOEXEC; + } + int fileDescriptor = socket(AF_UNIX, socketFlags, 0); if (fileDescriptor == -1) { return -1; } @@ -159,7 +163,7 @@ static bool checkSocketsDirectory() return true; } -XwaylandSocket::XwaylandSocket() +XwaylandSocket::XwaylandSocket(OperationMode mode) { if (!checkSocketsDirectory()) { return; @@ -173,13 +177,13 @@ XwaylandSocket::XwaylandSocket() continue; } - const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix); + const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode); if (unixFileDescriptor == -1) { QFile::remove(lockFilePath); continue; } - const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract); + const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode); if (abstractFileDescriptor == -1) { QFile::remove(lockFilePath); QFile::remove(socketFilePath); diff --git a/src/xwl/xwaylandsocket.h b/src/xwl/lib/xwaylandsocket.h similarity index 81% rename from src/xwl/xwaylandsocket.h rename to src/xwl/lib/xwaylandsocket.h index 77c73cf27f..0552d077db 100644 --- a/src/xwl/xwaylandsocket.h +++ b/src/xwl/lib/xwaylandsocket.h @@ -15,7 +15,12 @@ namespace KWin class XwaylandSocket { public: - XwaylandSocket(); + enum class OperationMode { + CloseFdsOnExec, + TransferFdsOnExec + }; + + XwaylandSocket(OperationMode operationMode); ~XwaylandSocket(); bool isValid() const; diff --git a/src/xwl/xwayland.cpp b/src/xwl/xwayland.cpp index 5ea7adf0fb..0616e3d064 100644 --- a/src/xwl/xwayland.cpp +++ b/src/xwl/xwayland.cpp @@ -10,7 +10,6 @@ */ #include "xwayland.h" #include "databridge.h" -#include "xwaylandsocket.h" #include "main_wayland.h" #include "options.h" @@ -19,6 +18,8 @@ #include "xcbutils.h" #include "xwayland_logging.h" +#include "xwaylandsocket.h" + #include #include #include @@ -68,119 +69,50 @@ QProcess *Xwayland::process() const return m_xwaylandProcess; } -static void writeXauthorityEntry(QDataStream &stream, quint16 family, - const QByteArray &address, const QByteArray &display, - const QByteArray &name, const QByteArray &cookie) -{ - stream << quint16(family); - - auto writeArray = [&stream](const QByteArray &str) { - stream << quint16(str.size()); - stream.writeRawData(str.constData(), str.size()); - }; - - writeArray(address); - writeArray(display); - writeArray(name); - writeArray(cookie); -} - -static QByteArray generateXauthorityCookie() -{ - QByteArray cookie; - cookie.resize(16); // Cookie must be 128bits - - QRandomGenerator *generator = QRandomGenerator::system(); - for (int i = 0; i < cookie.size(); ++i) { - cookie[i] = uint8_t(generator->bounded(256)); - } - return cookie; -} - -static bool generateXauthorityFile(int display, QTemporaryFile *authorityFile) -{ - const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - - authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX")); - if (!authorityFile->open()) { - return false; - } - - const QByteArray hostname = QHostInfo::localHostName().toUtf8(); - const QByteArray displayName = QByteArray::number(display); - const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); - const QByteArray cookie = generateXauthorityCookie(); - - QDataStream stream(authorityFile); - stream.setByteOrder(QDataStream::BigEndian); - - // Write entry with FamilyLocal and the host name as address - writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie); - - // Write entry with FamilyWild, no address - writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie); - - if (stream.status() != QDataStream::Ok || !authorityFile->flush()) { - authorityFile->remove(); - return false; - } - - return true; -} - void Xwayland::start() { if (m_xwaylandProcess) { return; } - QScopedPointer socket(new XwaylandSocket()); - if (!socket->isValid()) { - qCWarning(KWIN_XWL) << "Failed to create Xwayland connection sockets"; - Q_EMIT errorOccurred(); - return; - } - - if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) { - if (!generateXauthorityFile(socket->display(), &m_authorityFile)) { - qCWarning(KWIN_XWL) << "Failed to create an Xauthority file"; - Q_EMIT errorOccurred(); - return; + if (!m_listenFds.isEmpty()) { + Q_ASSERT(!m_displayName.isEmpty()); + } else { + m_socket.reset(new XwaylandSocket(XwaylandSocket::OperationMode::CloseFdsOnExec)); + if (!m_socket->isValid()) { + qFatal("Failed to establish X11 socket"); } + setListenFDs({m_socket->unixFileDescriptor(), m_socket->abstractFileDescriptor()}); + m_displayName = m_socket->name(); } - m_socket.reset(socket.take()); + startInternal(); +} - if (!startInternal()) { - m_authorityFile.remove(); - m_socket.reset(); - } +void Xwayland::setListenFDs(const QVector &listenFds) +{ + m_listenFds = listenFds; +} + +void Xwayland::setDisplayName(const QString &displayName) +{ + m_displayName = displayName; +} + +void Xwayland::setXauthority(const QString &xauthority) +{ + m_xAuthority = xauthority; } bool Xwayland::startInternal() { Q_ASSERT(!m_xwaylandProcess); - // The abstract socket file descriptor will be passed to Xwayland and closed by us. - const int abstractSocket = dup(m_socket->abstractFileDescriptor()); - if (abstractSocket == -1) { - qCWarning(KWIN_XWL, "Failed to duplicate file descriptor: %s", strerror(errno)); - Q_EMIT errorOccurred(); - return false; - } - auto abstractSocketCleanup = qScopeGuard([&abstractSocket]() { - close(abstractSocket); - }); - - // The unix socket file descriptor will be passed to Xwayland and closed by us. - const int unixSocket = dup(m_socket->unixFileDescriptor()); - if (unixSocket == -1) { - qCWarning(KWIN_XWL, "Failed to duplicate file descriptor: %s", strerror(errno)); - Q_EMIT errorOccurred(); - return false; - } - auto unixSocketCleanup = qScopeGuard([&unixSocket]() { - close(unixSocket); + QVector fdsToClose; + auto cleanup = qScopeGuard([&fdsToClose] { + for (const int fd : qAsConst(fdsToClose)) { + close(fd); + } }); int pipeFds[2]; @@ -189,6 +121,8 @@ bool Xwayland::startInternal() Q_EMIT errorOccurred(); return false; } + fdsToClose << pipeFds[1]; + int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { qCWarning(KWIN_XWL, "Failed to open socket for XCB connection: %s", strerror(errno)); @@ -217,24 +151,30 @@ bool Xwayland::startInternal() m_xcbConnectionFd = sx[0]; - QStringList arguments { - m_socket->name(), - QStringLiteral("-displayfd"), QString::number(pipeFds[1]), - QStringLiteral("-rootless"), - QStringLiteral("-wm"), QString::number(fd), - }; + QStringList arguments; - if (m_authorityFile.isOpen()) { - arguments << QStringLiteral("-auth") << m_authorityFile.fileName(); + arguments << m_displayName; + + if (!m_listenFds.isEmpty()) { + // xauthority externally set and managed + if (!m_xAuthority.isEmpty()) { + arguments << QStringLiteral("-auth") << m_xAuthority; + } + + for (int socket : qAsConst(m_listenFds)) { + int dupSocket = dup(socket); + fdsToClose << dupSocket; + #if defined(HAVE_XWAYLAND_LISTENFD) + arguments << QStringLiteral("-listenfd") << QString::number(dupSocket); + #else + arguments << QStringLiteral("-listen") << QString::number(dupSocket) + #endif + } } -#if defined(HAVE_XWAYLAND_LISTENFD) - arguments << QStringLiteral("-listenfd") << QString::number(abstractSocket) - << QStringLiteral("-listenfd") << QString::number(unixSocket); -#else - arguments << QStringLiteral("-listen") << QString::number(abstractSocket) - << QStringLiteral("-listen") << QString::number(unixSocket); -#endif + arguments << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + arguments << QStringLiteral("-rootless"); + arguments << QStringLiteral("-wm") << QString::number(fd); m_xwaylandProcess = new Process(this); m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); @@ -257,7 +197,6 @@ bool Xwayland::startInternal() connect(m_readyNotifier, &QSocketNotifier::activated, this, &Xwayland::handleXwaylandReady); m_xwaylandProcess->start(); - close(pipeFds[1]); return true; } @@ -269,9 +208,6 @@ void Xwayland::stop() } stopInternal(); - - m_socket.reset(); - m_authorityFile.remove(); } void Xwayland::stopInternal() @@ -435,13 +371,7 @@ void Xwayland::handleXwaylandReady() return; } - const QByteArray displayName = ':' + QByteArray::number(m_socket->display()); - - qCInfo(KWIN_XWL) << "Xwayland server started on display" << displayName; - qputenv("DISPLAY", displayName); - if (m_authorityFile.isOpen()) { - qputenv("XAUTHORITY", m_authorityFile.fileName().toUtf8()); - } + qCInfo(KWIN_XWL) << "Xwayland server started on display" << m_displayName; // create selection owner for WM_S0 - magic X display number expected by XWayland m_selectionOwner.reset(new KSelectionOwner("WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow())); @@ -456,10 +386,8 @@ void Xwayland::handleXwaylandReady() DataBridge::create(this); auto env = m_app->processStartupEnvironment(); - env.insert(QStringLiteral("DISPLAY"), displayName); - if (m_authorityFile.isOpen()) { - env.insert(QStringLiteral("XAUTHORITY"), m_authorityFile.fileName()); - } + env.insert(QStringLiteral("DISPLAY"), m_displayName); + env.insert(QStringLiteral("XAUTHORITY"), m_xAuthority); m_app->setProcessStartupEnvironment(env); Xcb::sync(); // Trigger possible errors, there's still a chance to abort diff --git a/src/xwl/xwayland.h b/src/xwl/xwayland.h index b2eb642c59..b16338f597 100644 --- a/src/xwl/xwayland.h +++ b/src/xwl/xwayland.h @@ -39,6 +39,27 @@ public: */ QProcess *process() const override; + /** + * Set file descriptors that xwayland should use for listening + * This is to be used in conjuction with kwin_wayland_wrapper which creates a socket externally + * That external process is responsible for setting up the DISPLAY env with a valid value. + * Ownership of the file descriptor is not transferrred. + */ + void setListenFDs(const QVector &listenFds); + + /** + * Sets the display name used by XWayland (i.e ':0') + * This is to be used in conjuction with kwin_wayland_wrapper to provide the name of the socket + * created externally + */ + void setDisplayName(const QString &displayName); + + /** + * Sets the xauthority file to be used by XWayland + * This is to be used in conjuction with kwin_wayland_wrapper + */ + void setXauthority(const QString &xauthority); + public Q_SLOTS: /** * Starts the Xwayland server. @@ -111,8 +132,13 @@ private: QTimer *m_resetCrashCountTimer = nullptr; ApplicationWaylandAbstract *m_app; QScopedPointer m_selectionOwner; - QTemporaryFile m_authorityFile; + // this is only used when kwin is run without kwin_wayland_wrapper QScopedPointer m_socket; + + QVector m_listenFds; + QString m_displayName; + QString m_xAuthority; + int m_crashCount = 0; Q_DISABLE_COPY(Xwayland)