summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@qt.io>2020-08-13 13:18:14 +0200
committerRobert Griebl <robert.griebl@qt.io>2020-09-23 17:02:25 +0200
commit01102cbf25e6479192a59e23e68165d81f365420 (patch)
treede8bdf48cd37a8d8bf86cfeddfa01071532bf0fa
parent5820ee2dc4f0dcf1a1d2679351cfae293a5143f3 (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.qdoc21
-rw-r--r--src/main-lib/configuration.cpp41
-rw-r--r--src/main-lib/configuration.h1
-rw-r--r--src/main-lib/configuration_p.h5
-rw-r--r--src/main-lib/main.cpp70
-rw-r--r--src/main-lib/main.h2
-rw-r--r--src/manager-lib/sudo.cpp7
-rw-r--r--src/tools/appman/appman.cpp2
-rw-r--r--src/window-lib/windowmanager.cpp28
-rw-r--r--src/window-lib/windowmanager.h3
-rw-r--r--src/window-lib/windowmanager_p.h1
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