diff options
author | Robert Griebl <robert.griebl@qt.io> | 2020-08-13 13:18:14 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@qt.io> | 2020-09-23 17:02:25 +0200 |
commit | 01102cbf25e6479192a59e23e68165d81f365420 (patch) | |
tree | de8bdf48cd37a8d8bf86cfeddfa01071532bf0fa | |
parent | 5820ee2dc4f0dcf1a1d2679351cfae293a5143f3 (diff) |
Add support for additional Wayland sockets
This extension allows the user to specify additional Wayland sockets that
are not restricted by the $XDG_RUNTIME_DIR specification.
Change-Id: Ia915a3db459af646877a17d6f44758a6e11f2d45
Reviewed-by: Bernd Weimer <bernd.weimer@pelagicore.com>
-rw-r--r-- | doc/configuration.qdoc | 21 | ||||
-rw-r--r-- | src/main-lib/configuration.cpp | 41 | ||||
-rw-r--r-- | src/main-lib/configuration.h | 1 | ||||
-rw-r--r-- | src/main-lib/configuration_p.h | 5 | ||||
-rw-r--r-- | src/main-lib/main.cpp | 70 | ||||
-rw-r--r-- | src/main-lib/main.h | 2 | ||||
-rw-r--r-- | src/manager-lib/sudo.cpp | 7 | ||||
-rw-r--r-- | src/tools/appman/appman.cpp | 2 | ||||
-rw-r--r-- | src/window-lib/windowmanager.cpp | 28 | ||||
-rw-r--r-- | src/window-lib/windowmanager.h | 3 | ||||
-rw-r--r-- | src/window-lib/windowmanager_p.h | 1 |
11 files changed, 171 insertions, 10 deletions
diff --git a/doc/configuration.qdoc b/doc/configuration.qdoc index e6c4b2f0..38271118 100644 --- a/doc/configuration.qdoc +++ b/doc/configuration.qdoc @@ -408,6 +408,7 @@ or across multiple config files, the final value is resolved based on these rule is expensive, resource-wise. \row \li \b --wayland-socket-name + \br [\c wayland/socketName] \li string \li A filesystem name for the Wayland socket that should be used when creating the compositor component. (default: auto-detected by \c libwayland-server, most likely @@ -415,7 +416,25 @@ or across multiple config files, the final value is resolved based on these rule \note You can only specify the name here, but not a path: Wayland will always create this socket in \c{$XDG_RUNTIME_DIR}. The compositor will fail to start if this directory is not writable or if the environment variable is not even set in the - first place. + first place. See \c wayland/extraSockets below for a way to circumvent this + limitation. + \row + \li [\c wayland/extraSockets] + \li list<object> + \li A list of additional sockets that are added to the Wayland server. Each object in the + list consists of the following fields + \list + \li \c path: the absolute path of the socket. + \li \c permissions: the numeric permission of the socket - can be given in octal, e.g. \c 0640. + \li \c userId: the numeric uid of the socket. + \li \c groupId: the numeric gid of the socket. + \endlist + Only \c path is required, while the other fields are optional and fall back to + QLocalServer defaults. + + If the application manager is runing under \c sudo or with setuid-root, it will use + its extended privileges to apply the optional \c permissions, \c userId and \c groupId + settings. \row \li \b --disable-installer \br [\c installer/disable] diff --git a/src/main-lib/configuration.cpp b/src/main-lib/configuration.cpp index c19e0b8b..fb50ae88 100644 --- a/src/main-lib/configuration.cpp +++ b/src/main-lib/configuration.cpp @@ -399,7 +399,7 @@ void Configuration::parseWithArguments(const QStringList &arguments) } -const quint32 ConfigurationData::DataStreamVersion = 2; +const quint32 ConfigurationData::DataStreamVersion = 3; ConfigurationData *ConfigurationData::loadFromCache(QDataStream &ds) @@ -456,7 +456,10 @@ ConfigurationData *ConfigurationData::loadFromCache(QDataStream &ds) >> cd->flags.noUiWatchdog >> cd->flags.developmentMode >> cd->flags.forceMultiProcess - >> cd->flags.forceSingleProcess; + >> cd->flags.forceSingleProcess + >> cd->wayland.socketName + >> cd->wayland.extraSockets; + return cd; } @@ -513,7 +516,9 @@ void ConfigurationData::saveToCache(QDataStream &ds) const << flags.noUiWatchdog << flags.developmentMode << flags.forceMultiProcess - << flags.forceSingleProcess; + << flags.forceSingleProcess + << wayland.socketName + << wayland.extraSockets; } template <typename T> void mergeField(T &into, const T &from, const T &def) @@ -604,6 +609,8 @@ void ConfigurationData::mergeFrom(const ConfigurationData *from) MERGE_FIELD(flags.developmentMode); MERGE_FIELD(flags.forceMultiProcess); MERGE_FIELD(flags.forceSingleProcess); + MERGE_FIELD(wayland.socketName); + MERGE_FIELD(wayland.extraSockets); } QByteArray ConfigurationData::substituteVars(const QByteArray &sourceContent, const QString &fileName) @@ -813,6 +820,26 @@ ConfigurationData *ConfigurationData::loadFromSource(QIODevice *source, const QS { "noUiWatchdog", false, YamlParser::Scalar, [&cd](YamlParser *p) { cd->flags.noUiWatchdog = p->parseScalar().toBool(); } }, }); } }, + { "wayland", false, YamlParser::Map, [&cd](YamlParser *p) { + p->parseFields({ + { "socketName", false, YamlParser::Scalar, [&cd](YamlParser *p) { + cd->wayland.socketName = p->parseScalar().toString(); } }, + { "extraSockets", false, YamlParser::List, [&cd](YamlParser *p) { + p->parseList([&cd](YamlParser *p) { + QVariantMap wes; + p->parseFields({ + { "path", true, YamlParser::Scalar, [&wes](YamlParser *p) { + wes.insert(qSL("path"), p->parseScalar().toString()); } }, + { "permissions", false, YamlParser::Scalar, [&wes](YamlParser *p) { + wes.insert(qSL("permissions"), p->parseScalar().toInt()); } }, + { "userId", false, YamlParser::Scalar, [&wes](YamlParser *p) { + wes.insert(qSL("userId"), p->parseScalar().toInt()); } }, + { "groupId", false, YamlParser::Scalar, [&wes](YamlParser *p) { + wes.insert(qSL("groupId"), p->parseScalar().toInt()); } } + }); + cd->wayland.extraSockets.append(wes); + }); } } + }); } }, { "systemProperties", false, YamlParser::Map, [&cd](YamlParser *p) { cd->systemProperties = p->parseMap(); } }, { "crashAction", false, YamlParser::Map, [&cd](YamlParser *p) { @@ -1202,6 +1229,9 @@ QString Configuration::waylandSocketName() const return socketName; } + if (!m_data->wayland.socketName.isEmpty()) + return m_data->wayland.socketName; + # if defined(Q_OS_LINUX) // modelled after wl_socket_lock() in wayland_server.c const QString xdgDir = qEnvironmentVariable("XDG_RUNTIME_DIR") + qSL("/"); @@ -1225,6 +1255,11 @@ QString Configuration::waylandSocketName() const } +QVariantList Configuration::waylandExtraSockets() const +{ + return m_data->wayland.extraSockets; +} + QVariantMap Configuration::managerCrashAction() const { return m_data->crashAction; diff --git a/src/main-lib/configuration.h b/src/main-lib/configuration.h index fc96e67e..a3d3bb0d 100644 --- a/src/main-lib/configuration.h +++ b/src/main-lib/configuration.h @@ -129,6 +129,7 @@ public: int quickLaunchRuntimesPerContainer() const; QString waylandSocketName() const; + QVariantList waylandExtraSockets() const; QVariantMap managerCrashAction() const; diff --git a/src/main-lib/configuration_p.h b/src/main-lib/configuration_p.h index 08771523..684e2fb7 100644 --- a/src/main-lib/configuration_p.h +++ b/src/main-lib/configuration_p.h @@ -156,6 +156,11 @@ struct ConfigurationData bool noUiWatchdog = false; } flags; + struct { + QString socketName; + QVariantList extraSockets; + } wayland; + // command-line only: struct { QString waylandSocketName; diff --git a/src/main-lib/main.cpp b/src/main-lib/main.cpp index d28b4195..1c4864dd 100644 --- a/src/main-lib/main.cpp +++ b/src/main-lib/main.cpp @@ -79,6 +79,7 @@ # include <QQuickItem> # include <QTouchDevice> # include <private/qopenglcontext_p.h> +# include <QLocalServer> #endif #if defined(QT_PSHELLSERVER_LIB) @@ -280,7 +281,8 @@ void Main::setup(const Configuration *cfg) Q_DECL_NOEXCEPT_EXPR(false) setLibraryPaths(libraryPaths() + cfg->pluginPaths()); setupQmlEngine(cfg->importPaths(), cfg->style()); setupWindowTitle(QString(), cfg->windowIcon()); - setupWindowManager(cfg->waylandSocketName(), cfg->slowAnimations(), cfg->noUiWatchdog()); + setupWindowManager(cfg->waylandSocketName(), cfg->waylandExtraSockets(), + cfg->slowAnimations(), cfg->noUiWatchdog()); setupTouchEmulation(cfg->enableTouchEmulation()); setupShellServer(QString(), 0); // remove setupSSDPService(); @@ -678,7 +680,8 @@ void Main::setupWindowTitle(const QString &title, const QString &iconPath) #endif // AM_HEADLESS } -void Main::setupWindowManager(const QString &waylandSocketName, bool slowAnimations, bool uiWatchdog) +void Main::setupWindowManager(const QString &waylandSocketName, const QVariantList &waylandExtraSockets, + bool slowAnimations, bool uiWatchdog) { #if defined(AM_HEADLESS) Q_UNUSED(waylandSocketName) @@ -691,6 +694,69 @@ void Main::setupWindowManager(const QString &waylandSocketName, bool slowAnimati m_windowManager->setSlowAnimations(slowAnimations); m_windowManager->enableWatchdog(!uiWatchdog); +#if defined(QT_WAYLANDCOMPOSITOR_LIB) + for (const auto &v : waylandExtraSockets) { + const QVariantMap &wes = v.toMap(); + + const QString path = wes.value(qSL("path")).toString(); + + if (path.isEmpty()) + continue; + QFileInfo fi(path); + if (!fi.dir().mkpath(qSL("."))) + throw Exception("could not create path to extra Wayland socket: %1").arg(path); + + auto sudo = SudoClient::instance(); + + if (!sudo || sudo->isFallbackImplementation()) + QLocalServer::removeServer(path); // if it fails, we'll notice in listen() right away + else + sudo->removeRecursive(path); + + QScopedPointer<QLocalServer> extraSocket(new QLocalServer); + extraSocket->setMaxPendingConnections(0); // disable Qt's new connection handling + if (!extraSocket->listen(path)) { + throw Exception("could not listen on extra Wayland socket %1: %2") + .arg(path, extraSocket->errorString()); + } + int mode = wes.value(qSL("permissions"), -1).toInt(); + int uid = wes.value(qSL("userId"), -1).toInt(); + int gid = wes.value(qSL("groupId"), -1).toInt(); + + QByteArray encodedPath = QFile::encodeName(path); + + if ((mode > 0) || (uid != -1) || (gid != -1)) { + if (!sudo || sudo->isFallbackImplementation()) { + if (mode > 0) { + if (::chmod(encodedPath, static_cast<mode_t>(mode)) != 0) + throw Exception(errno, "could not chmod(mode: %1) the extra Wayland socket %2") + .arg(QString::number(mode, 8), path); + } + if ((uid != -1) || (gid != -1)) { + if (::chown(encodedPath, static_cast<uid_t>(uid), static_cast<gid_t>(gid)) != 0) + throw Exception(errno, "could not chown(uid: %1, gid: %2) the extra Wayland socket %3") + .arg(uid).arg(gid).arg(path); + } + } else { + if (!sudo->setOwnerAndPermissionsRecursive(path, static_cast<uid_t>(uid), + static_cast<gid_t>(gid), + static_cast<mode_t>(mode))) { + throw Exception(Error::IO, "could not change the owner to %1:%2 and the permission" + " bits to %3 for the extra Wayland socket %4: %5") + .arg(uid).arg(gid).arg(mode, 0, 8).arg(path).arg(sudo->lastError()); + } + // if we changed the owner, ~QLocalServer might not be able to clean up the + // socket inode, so we need to sudo this removal as well + QObject::connect(extraSocket.data(), &QObject::destroyed, [path, sudo]() { + sudo->removeRecursive(path); + }); + } + } + + m_windowManager->addWaylandSocket(extraSocket.take()); + } +#endif + QObject::connect(&m_applicationManager->internalSignals, &ApplicationManagerInternalSignals::newRuntimeCreated, m_windowManager, &WindowManager::setupInProcessRuntime); QObject::connect(m_applicationManager, &ApplicationManager::applicationWasActivated, diff --git a/src/main-lib/main.h b/src/main-lib/main.h index 5fe0abff..154d0e6b 100644 --- a/src/main-lib/main.h +++ b/src/main-lib/main.h @@ -129,7 +129,7 @@ protected: void setupQmlEngine(const QStringList &importPaths, const QString &quickControlsStyle = QString()); void setupWindowTitle(const QString &title, const QString &iconPath); - void setupWindowManager(const QString &waylandSocketName, bool slowAnimations, bool uiWatchdog); + void setupWindowManager(const QString &waylandSocketName, const QVariantList &waylandExtraSockets, bool slowAnimations, bool uiWatchdog); void setupTouchEmulation(bool enableTouchEmulation); void setupShellServer(const QString &telnetAddress, quint16 telnetPort) Q_DECL_NOEXCEPT_EXPR(false); diff --git a/src/manager-lib/sudo.cpp b/src/manager-lib/sudo.cpp index d26caedc..26602c28 100644 --- a/src/manager-lib/sudo.cpp +++ b/src/manager-lib/sudo.cpp @@ -463,6 +463,7 @@ bool SudoServer::setOwnerAndPermissionsRecursive(const QString &fileOrDir, uid_t return true; const QByteArray localPath = path.toLocal8Bit(); + bool noModeChange = (permissions == static_cast<mode_t>(-1)); mode_t mode = permissions; if (type == RecursiveOperationType::LeaveDirectory) { @@ -475,7 +476,8 @@ bool SudoServer::setOwnerAndPermissionsRecursive(const QString &fileOrDir, uid_t mode |= 0100; } - return ((chmod(localPath, mode) == 0) && (chown(localPath, user, group) == 0)); + return ((noModeChange ? true : (chmod(localPath, mode) == 0)) + && (chown(localPath, user, group) == 0)); }; try { @@ -483,6 +485,7 @@ bool SudoServer::setOwnerAndPermissionsRecursive(const QString &fileOrDir, uid_t throw Exception(errno, "could not recursively set owner and permission on %1 to %2:%3 / %4") .arg(fileOrDir).arg(user).arg(group).arg(permissions, 4, 8, QLatin1Char('0')); } + return true; } catch (const Exception &e) { m_errorString = e.errorString(); return false; @@ -492,8 +495,8 @@ bool SudoServer::setOwnerAndPermissionsRecursive(const QString &fileOrDir, uid_t Q_UNUSED(user) Q_UNUSED(group) Q_UNUSED(permissions) -#endif // Q_OS_LINUX return false; +#endif // Q_OS_LINUX } QT_END_NAMESPACE_AM diff --git a/src/tools/appman/appman.cpp b/src/tools/appman/appman.cpp index 42bff23e..3d4e5617 100644 --- a/src/tools/appman/appman.cpp +++ b/src/tools/appman/appman.cpp @@ -128,7 +128,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) return MainBase::exec(); #endif } catch (const Exception &e) { - qCCritical(LogSystem) << "ERROR:" << e.errorString(); + qCCritical(LogSystem).noquote() << "ERROR:" << e.errorString(); return 2; } } diff --git a/src/window-lib/windowmanager.cpp b/src/window-lib/windowmanager.cpp index 72f8ee84..0d8c7e9c 100644 --- a/src/window-lib/windowmanager.cpp +++ b/src/window-lib/windowmanager.cpp @@ -51,6 +51,7 @@ #include <QThread> #include <QQmlComponent> #include <private/qabstractanimation_p.h> +#include <QLocalServer> #if defined(AM_MULTI_PROCESS) # include "waylandcompositor.h" @@ -406,6 +407,30 @@ void WindowManager::enableWatchdog(bool enable) #endif } +bool WindowManager::addWaylandSocket(QLocalServer *waylandSocket) +{ +#if defined(AM_MULTI_PROCESS) + if (d->waylandCompositor) { + qCWarning(LogGraphics) << "Cannot add extra Wayland sockets after the compositor has been created" + " (tried to add:" << waylandSocket->fullServerName() << ")."; + delete waylandSocket; + return false; + } + + if (!waylandSocket || (waylandSocket->socketDescriptor() < 0)) { + delete waylandSocket; + return false; + } + + waylandSocket->setParent(this); + d->extraWaylandSockets << waylandSocket->socketDescriptor(); + return true; +#else + Q_UNUSED(waylandSocket) + return true; +#endif +} + int WindowManager::rowCount(const QModelIndex &parent) const { if (parent.isValid()) @@ -663,6 +688,9 @@ void WindowManager::registerCompositorView(QQuickWindow *view) if (!ApplicationManager::instance()->isSingleProcess()) { if (!d->waylandCompositor) { d->waylandCompositor = new WaylandCompositor(view, d->waylandSocketName); + for (const auto &extraSocket : d->extraWaylandSockets) + d->waylandCompositor->addSocketDescriptor(extraSocket); + connect(d->waylandCompositor, &QWaylandCompositor::surfaceCreated, this, &WindowManager::waylandSurfaceCreated); connect(d->waylandCompositor, &WaylandCompositor::surfaceMapped, diff --git a/src/window-lib/windowmanager.h b/src/window-lib/windowmanager.h index 6e6d5982..466275fd 100644 --- a/src/window-lib/windowmanager.h +++ b/src/window-lib/windowmanager.h @@ -56,6 +56,7 @@ QT_FORWARD_DECLARE_CLASS(QQmlEngine) QT_FORWARD_DECLARE_CLASS(QJSEngine) QT_FORWARD_DECLARE_CLASS(QWindow) QT_FORWARD_DECLARE_CLASS(QQmlComponent) +QT_FORWARD_DECLARE_CLASS(QLocalServer) QT_BEGIN_NAMESPACE_AM @@ -91,6 +92,8 @@ public: void enableWatchdog(bool enable); + bool addWaylandSocket(QLocalServer *waylandSocket); + // the item model part int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; diff --git a/src/window-lib/windowmanager_p.h b/src/window-lib/windowmanager_p.h index f692e765..8bf93300 100644 --- a/src/window-lib/windowmanager_p.h +++ b/src/window-lib/windowmanager_p.h @@ -61,6 +61,7 @@ public: int findWindowByWaylandSurface(QWaylandSurface *waylandSurface) const; WaylandCompositor *waylandCompositor = nullptr; + QVector<int> extraWaylandSockets; static QString applicationId(Application *app, WindowSurface *windowSurface); #endif |