diff --git a/autotests/integration/kwin_wayland_test.cpp b/autotests/integration/kwin_wayland_test.cpp index 661eafc128..3e0ed8308e 100644 --- a/autotests/integration/kwin_wayland_test.cpp +++ b/autotests/integration/kwin_wayland_test.cpp @@ -104,6 +104,7 @@ void WaylandTestApplication::performStartup() environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); environment.remove("DISPLAY"); environment.remove("WAYLAND_DISPLAY"); + environment.remove("XAUTHORITY"); QProcess *p = new Process(this); p->setProcessChannelMode(QProcess::ForwardedErrorChannel); connect(p, qOverload(&QProcess::finished), this, diff --git a/main_wayland.cpp b/main_wayland.cpp index 90344f3870..0abe4bee46 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -271,6 +271,7 @@ void ApplicationWayland::startInputMethod(const QString &executable) environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); environment.remove("DISPLAY"); environment.remove("WAYLAND_DISPLAY"); + environment.remove("XAUTHORITY"); m_inputMethodProcess = new Process(this); m_inputMethodProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); diff --git a/xwl/xwayland.cpp b/xwl/xwayland.cpp index a24945e389..e306b220e9 100644 --- a/xwl/xwayland.cpp +++ b/xwl/xwayland.cpp @@ -23,7 +23,10 @@ #include #include +#include #include +#include +#include #include #include @@ -39,22 +42,20 @@ #include #include -static QByteArray readDisplay(int pipe) +static int readDisplay(int pipe) { - QByteArray displayName; + int display = -1; QFile readPipe; if (!readPipe.open(pipe, QIODevice::ReadOnly)) { qCWarning(KWIN_XWL) << "Failed to open X11 display name pipe:" << readPipe.errorString(); } else { - displayName = readPipe.readLine(); - displayName.prepend(QByteArrayLiteral(":")); - displayName.remove(displayName.size() - 1, 1); + display = readPipe.readLine().trimmed().toInt(); } // close our pipe close(pipe); - return displayName; + return display; } namespace KWin @@ -119,6 +120,12 @@ void Xwayland::start() return; } + if (!createXauthorityFile()) { + qCWarning(KWIN_XWL) << "Failed to create an Xauthority file"; + emit errorOccurred(); + return; + } + m_xcbConnectionFd = sx[0]; m_displayFileDescriptor = pipeFds[0]; @@ -135,8 +142,8 @@ void Xwayland::start() m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), QString::number(pipeFds[1]), QStringLiteral("-rootless"), - QStringLiteral("-wm"), - QString::number(fd)}); + QStringLiteral("-wm"), QString::number(fd), + QStringLiteral("-auth"), m_authorityFile->fileName()}); connect(m_xwaylandProcess, &QProcess::errorOccurred, this, &Xwayland::handleXwaylandError); connect(m_xwaylandProcess, &QProcess::started, this, &Xwayland::handleXwaylandStarted); connect(m_xwaylandProcess, QOverload::of(&QProcess::finished), @@ -173,6 +180,7 @@ void Xwayland::stop() delete m_xwaylandProcess; m_xwaylandProcess = nullptr; + m_authorityFile.reset(); waylandServer()->destroyXWaylandConnection(); // This one must be destroyed last! m_app->setClosingX11Connection(false); @@ -233,8 +241,8 @@ void Xwayland::uninstallSocketNotifier() void Xwayland::handleXwaylandStarted() { - m_watcher = new QFutureWatcher(this); - connect(m_watcher, &QFutureWatcher::finished, this, &Xwayland::handleXwaylandReady); + m_watcher = new QFutureWatcher(this); + connect(m_watcher, &QFutureWatcher::finished, this, &Xwayland::handleXwaylandReady); m_watcher->setFuture(QtConcurrent::run(readDisplay, m_displayFileDescriptor)); } @@ -305,7 +313,7 @@ void Xwayland::handleXwaylandError(QProcess::ProcessError error) void Xwayland::handleXwaylandReady() { - m_displayName = m_watcher->result(); + m_display = m_watcher->result(); m_watcher->deleteLater(); m_watcher = nullptr; @@ -315,8 +323,12 @@ void Xwayland::handleXwaylandReady() return; } - qCInfo(KWIN_XWL) << "Xwayland server started on display" << m_displayName; - qputenv("DISPLAY", m_displayName); + const QByteArray displayName = ':' + QByteArray::number(m_display); + updateXauthorityFile(); + + qCInfo(KWIN_XWL) << "Xwayland server started on display" << displayName; + qputenv("DISPLAY", displayName); + qputenv("XAUTHORITY", m_authorityFile->fileName().toUtf8()); // create selection owner for WM_S0 - magic X display number expected by XWayland m_selectionOwner.reset(new KSelectionOwner("WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow())); @@ -325,7 +337,8 @@ void Xwayland::handleXwaylandReady() DataBridge::create(this); auto env = m_app->processStartupEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_displayName); + env.insert(QStringLiteral("DISPLAY"), displayName); + env.insert(QStringLiteral("XAUTHORITY"), m_authorityFile->fileName()); m_app->setProcessStartupEnvironment(env); emit started(); @@ -386,6 +399,68 @@ void Xwayland::destroyX11Connection() emit m_app->x11ConnectionChanged(); } +bool Xwayland::createXauthorityFile() +{ + const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + const QString fileNameTemplate = QStringLiteral(".Xauthority-kwin_wayland.XXXXXX"); + + QScopedPointer authorityFile(new QTemporaryFile(runtimeDirectory + '/' + fileNameTemplate)); + if (!authorityFile->open()) { + return false; + } + + m_authorityFile.reset(authorityFile.take()); + return true; +} + +static void writeXauthorityEntry(QDataStream *stream, quint16 family, + const QByteArray &address, const QByteArray &display, + const QByteArray &name, const QByteArray &cookie) +{ + *stream << quint16(family); + + *stream << quint16(address.size()); + stream->writeRawData(address.constData(), address.size()); + + *stream << quint16(display.size()); + stream->writeRawData(display.constData(), display.size()); + + *stream << quint16(name.size()); + stream->writeRawData(name.constData(), name.size()); + + *stream << quint16(cookie.size()); + stream->writeRawData(cookie.constData(), cookie.size()); +} + +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; +} + +void Xwayland::updateXauthorityFile() +{ + const quint16 family = 256; // FamilyLocal + + const QByteArray address = QHostInfo::localHostName().toUtf8(); + const QByteArray display = QByteArray::number(m_display); + const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); + const QByteArray cookie = generateXauthorityCookie(); + + QDataStream stream(m_authorityFile.data()); + stream.setByteOrder(QDataStream::BigEndian); + + writeXauthorityEntry(&stream, family, address, display, name, cookie); + + m_authorityFile->flush(); +} + DragEventReply Xwayland::dragMoveFilter(Toplevel *target, const QPoint &pos) { DataBridge *bridge = DataBridge::self(); diff --git a/xwl/xwayland.h b/xwl/xwayland.h index 274aafcf7c..a646dcf467 100644 --- a/xwl/xwayland.h +++ b/xwl/xwayland.h @@ -15,6 +15,7 @@ #include #include #include +#include class KSelectionOwner; @@ -97,6 +98,9 @@ private: bool createX11Connection(); void destroyX11Connection(); + bool createXauthorityFile(); + void updateXauthorityFile(); + DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos) override; int m_displayFileDescriptor = -1; @@ -104,10 +108,11 @@ private: QProcess *m_xwaylandProcess = nullptr; QSocketNotifier *m_socketNotifier = nullptr; QTimer *m_resetCrashCountTimer = nullptr; - QByteArray m_displayName; - QFutureWatcher *m_watcher = nullptr; + int m_display = -1; + QFutureWatcher *m_watcher = nullptr; ApplicationWaylandAbstract *m_app; QScopedPointer m_selectionOwner; + QScopedPointer m_authorityFile; int m_crashCount = 0; Q_DISABLE_COPY(Xwayland)