summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@qt.io>2024-02-06 17:23:27 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2024-02-07 18:24:56 +0000
commit90c4a323203f6223c78539183d183effd11f232b (patch)
treed6f2adf2c9bb58058b86b1ed4a7fad5aabbb70d7
parent563b91d2b310f4ef56909f50ce8eede9bed0b8e4 (diff)
Use a P2P DBus on non-Linux platforms for the appman-controller
Change-Id: I331a4221ecd92fcf4af67e4fe20546b4abef22bf Reviewed-by: Dominik Holland <dominik.holland@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> (cherry picked from commit 3a999a9ad6f27713d15a2bfac0c53380932b25f9) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/main-lib/main.cpp170
-rw-r--r--src/main-lib/main.h10
-rw-r--r--src/tools/controller/controller.cpp66
3 files changed, 177 insertions, 69 deletions
diff --git a/src/main-lib/main.cpp b/src/main-lib/main.cpp
index 7c3284bf..5c0111c9 100644
--- a/src/main-lib/main.cpp
+++ b/src/main-lib/main.cpp
@@ -12,6 +12,7 @@
#if defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
# include <QDBusConnection>
# include <QDBusAbstractAdaptor>
+# include <QDBusServer>
# include "dbusdaemon.h"
# include "dbuspolicy.h"
# include "dbuscontextadaptor.h"
@@ -107,70 +108,6 @@ AM_QML_REGISTER_TYPES(QtApplicationManager_Application)
QT_BEGIN_NAMESPACE_AM
-
-#if defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
-
-static QString registerDBusObject(QDBusAbstractAdaptor *adaptor, QString dbusName,
- const char *serviceName, const char *path) noexcept(false)
-{
- QString dbusAddress;
- QDBusConnection conn((QString()));
-
- if (dbusName.isEmpty()) {
- return { };
- } else if (dbusName == u"system") {
- dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SYSTEM_BUS_ADDRESS"));
-# if defined(Q_OS_LINUX)
- if (dbusAddress.isEmpty())
- dbusAddress = u"unix:path=/var/run/dbus/system_bus_socket"_s;
-# endif
- conn = QDBusConnection::systemBus();
- } else if (dbusName == u"session") {
- dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
- conn = QDBusConnection::sessionBus();
- } else if (dbusName == u"auto") {
- dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
- // we cannot be using QDBusConnection::sessionBus() here, because some plugin
- // might have called that function before we could spawn our own session bus. In
- // this case, Qt has cached the bus name and we would get the old one back.
- conn = QDBusConnection::connectToBus(dbusAddress, u"qtam_session"_s);
- if (!conn.isConnected())
- return { };
- dbusName = u"session"_s;
- } else {
- dbusAddress = dbusName;
- conn = QDBusConnection::connectToBus(dbusAddress, u"custom"_s);
- }
-
- if (!conn.isConnected()) {
- throw Exception("could not connect to D-Bus (%1): %2")
- .arg(dbusAddress.isEmpty() ? dbusName : dbusAddress).arg(conn.lastError().message());
- }
-
- if (adaptor->parent() && adaptor->parent()->parent()) {
- // we need this information later on to tell apps where services are listening
- adaptor->parent()->parent()->setProperty("_am_dbus_name", dbusName);
- adaptor->parent()->parent()->setProperty("_am_dbus_address", dbusAddress);
- }
-
- if (!conn.registerObject(QString::fromLatin1(path), adaptor->parent(), QDBusConnection::ExportAdaptors)) {
- throw Exception("could not register object %1 on D-Bus (%2): %3")
- .arg(QString::fromLatin1(path)).arg(dbusName).arg(conn.lastError().message());
- }
-
- if (!conn.registerService(QString::fromLatin1(serviceName))) {
- throw Exception("could not register service %1 on D-Bus (%2): %3")
- .arg(QString::fromLatin1(serviceName)).arg(dbusName).arg(conn.lastError().message());
- }
-
- qCDebug(LogSystem).nospace().noquote() << " * " << serviceName << path << " [on bus: " << dbusName << "]";
-
- return dbusAddress.isEmpty() ? dbusName : dbusAddress;
-}
-
-#endif // defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
-
-
// We need to do some things BEFORE the Q*Application constructor runs, so we're using this
// old trick to do this hooking transparently for the user of the class.
int &Main::preConstructor(int &argc, char **argv, InitFlags initFlags)
@@ -1000,10 +937,20 @@ void Main::setupDBus(const std::function<QString(const char *)> &busForInterface
DBusDaemonProcess::start();
StartupTimer::instance()->checkpoint("after starting session D-Bus");
} catch (const Exception &e) {
+# if defined(Q_OS_LINUX)
qCWarning(LogSystem) << "Disabling external D-Bus interfaces:" << e.what();
for (auto &&iface : ifaces)
std::get<1>(iface).clear();
noneOnly = true;
+# else
+ qCWarning(LogSystem) << "Could not start a private dbus-daemon:" << e.what();
+ qCWarning(LogSystem) << "Enabling DBus P2P access for appman-controller";
+ for (auto &&iface : ifaces) {
+ QString &dbusName = std::get<1>(iface);
+ if (dbusName == u"auto")
+ dbusName = u"p2p"_s;
+ }
+# endif
}
}
@@ -1034,6 +981,101 @@ void Main::setupDBus(const std::function<QString(const char *)> &busForInterface
#else
Q_UNUSED(busForInterface)
Q_UNUSED(policyForInterface)
+ Q_UNUSED(m_p2pServer)
+ Q_UNUSED(m_p2pAdaptors)
+ Q_UNUSED(m_p2pFailed)
+#endif // defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
+}
+
+QString Main::registerDBusObject(QDBusAbstractAdaptor *adaptor, QString dbusName,
+ const char *serviceName, const char *path) noexcept(false)
+{
+#if defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
+ QString dbusAddress;
+ QDBusConnection conn((QString()));
+ bool isP2P = false;
+
+ if (dbusName.isEmpty()) {
+ return { };
+ } else if (dbusName == u"system") {
+ dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SYSTEM_BUS_ADDRESS"));
+# if defined(Q_OS_LINUX)
+ if (dbusAddress.isEmpty())
+ dbusAddress = u"unix:path=/var/run/dbus/system_bus_socket"_s;
+# endif
+ conn = QDBusConnection::systemBus();
+ } else if (dbusName == u"session") {
+ dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
+ conn = QDBusConnection::sessionBus();
+ } else if (dbusName == u"auto") {
+ dbusAddress = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
+ // we cannot be using QDBusConnection::sessionBus() here, because some plugin
+ // might have called that function before we could spawn our own session bus. In
+ // this case, Qt has cached the bus name and we would get the old one back.
+ conn = QDBusConnection::connectToBus(dbusAddress, u"qtam_session"_s);
+ if (!conn.isConnected())
+ return { };
+ dbusName = u"session"_s;
+ } else if (dbusName == u"p2p") {
+ if (!m_p2pServer && !m_p2pFailed) {
+ m_p2pServer = new QDBusServer(this);
+ m_p2pServer->setAnonymousAuthenticationAllowed(true);
+
+ if (!m_p2pServer->isConnected()) {
+ m_p2pFailed = true;
+ delete m_p2pServer;
+ m_p2pServer = nullptr;
+ qCCritical(LogSystem) << "Failed to create a P2P DBus server for appman-controller";
+ } else {
+ QObject::connect(m_p2pServer, &QDBusServer::newConnection,
+ this, [this](const QDBusConnection &conn) {
+ for (const auto &[path, object] : std::as_const(m_p2pAdaptors).asKeyValueRange())
+ object->registerOnDBus(conn, path);
+ });
+ }
+ }
+ if (m_p2pFailed)
+ return { };
+ m_p2pAdaptors.insert(QString::fromLatin1(path), qobject_cast<DBusContextAdaptor *>(adaptor->parent()));
+ dbusAddress = u"p2p:"_s + m_p2pServer->address();
+ isP2P = true;
+ } else {
+ dbusAddress = dbusName;
+ conn = QDBusConnection::connectToBus(dbusAddress, u"custom"_s);
+ }
+
+ if (!isP2P) {
+ if (!conn.isConnected()) {
+ throw Exception("could not connect to D-Bus (%1): %2")
+ .arg(dbusAddress.isEmpty() ? dbusName : dbusAddress).arg(conn.lastError().message());
+ }
+
+ if (!conn.registerObject(QString::fromLatin1(path), adaptor->parent(), QDBusConnection::ExportAdaptors)) {
+ throw Exception("could not register object %1 on D-Bus (%2): %3")
+ .arg(QString::fromLatin1(path)).arg(dbusName).arg(conn.lastError().message());
+ }
+
+ if (!conn.registerService(QString::fromLatin1(serviceName))) {
+ throw Exception("could not register service %1 on D-Bus (%2): %3")
+ .arg(QString::fromLatin1(serviceName)).arg(dbusName).arg(conn.lastError().message());
+ }
+ }
+
+ if (adaptor->parent() && adaptor->parent()->parent()) {
+ // we need this information later on to tell apps where services are listening
+ adaptor->parent()->parent()->setProperty("_am_dbus_name", dbusName);
+ adaptor->parent()->parent()->setProperty("_am_dbus_address", dbusAddress);
+ }
+
+ qCDebug(LogSystem).nospace().noquote() << " * " << serviceName << path << " [on bus: " << dbusName << "]";
+
+ return dbusAddress.isEmpty() ? dbusName : dbusAddress;
+#else
+ Q_UNUSED(adaptor)
+ Q_UNUSED(dbusName)
+ Q_UNUSED(serviceName)
+ Q_UNUSED(path)
+ return { };
#endif // defined(QT_DBUS_LIB) && QT_CONFIG(am_external_dbus_interfaces)
}
diff --git a/src/main-lib/main.h b/src/main-lib/main.h
index 9e280ce8..dfdd94b0 100644
--- a/src/main-lib/main.h
+++ b/src/main-lib/main.h
@@ -30,6 +30,7 @@ using MainBase = QGuiApplication;
QT_FORWARD_DECLARE_CLASS(QQmlApplicationEngine)
QT_FORWARD_DECLARE_CLASS(QQuickView)
QT_FORWARD_DECLARE_CLASS(QDBusAbstractAdaptor)
+QT_FORWARD_DECLARE_CLASS(QDBusServer)
class StartupInterface;
@@ -47,6 +48,8 @@ class WindowManager;
class QuickLauncher;
class SystemMonitor;
class Configuration;
+class DBusContextAdaptor;
+
class Main : public MainBase, protected SharedMain
{
@@ -115,6 +118,9 @@ protected:
private:
static int &preConstructor(int &argc, char **argv, InitFlags initFlags);
+ QString registerDBusObject(QDBusAbstractAdaptor *adaptor, QString dbusName,
+ const char *serviceName, const char *path) noexcept(false);
+
private:
bool m_isSingleProcessMode = false;
bool m_isRunningOnEmbedded = false;
@@ -141,6 +147,10 @@ private:
QString m_documentDir;
QString m_installationDirMountPoint;
QVariantMap m_infoFileContents;
+
+ QDBusServer *m_p2pServer = nullptr;
+ QHash<QString, DBusContextAdaptor *> m_p2pAdaptors;
+ bool m_p2pFailed = false;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Main::InitFlags)
diff --git a/src/tools/controller/controller.cpp b/src/tools/controller/controller.cpp
index de058775..48b0abe5 100644
--- a/src/tools/controller/controller.cpp
+++ b/src/tools/controller/controller.cpp
@@ -25,6 +25,7 @@
#include <QtAppManCommon/error.h>
#include <QtAppManCommon/exception.h>
#include <QtAppManCommon/unixsignalhandler.h>
+#include <QtAppManCommon/utilities.h>
#include <QtAppManCommon/qtyaml.h>
#include <QtAppManCommon/dbus-utilities.h>
@@ -43,6 +44,8 @@ class DBus : public QObject
public:
DBus()
+ : m_dbusService(u"io.qt.ApplicationManager"_s)
+
{
registerDBusTypes();
}
@@ -58,7 +61,13 @@ public:
return;
auto conn = connectTo(u"io.qt.ApplicationManager"_s);
- m_manager = new IoQtApplicationManagerInterface(u"io.qt.ApplicationManager"_s, u"/ApplicationManager"_s, conn, this);
+ m_manager = tryConnectToDBusInterface<IoQtApplicationManagerInterface>(m_dbusService,
+ u"/ApplicationManager"_s,
+ conn.name(), this);
+ if (!m_manager) {
+ throw Exception("Could not connect to the io.qt.ApplicationManager D-Bus interface on %1")
+ .arg(m_dbusName);
+ }
}
void connectToPackager() noexcept(false)
@@ -67,13 +76,52 @@ public:
return;
auto conn = connectTo(u"io.qt.PackageManager"_s);
- m_packager = new IoQtPackageManagerInterface(u"io.qt.ApplicationManager"_s, u"/PackageManager"_s, conn, this);
+ m_packager = tryConnectToDBusInterface<IoQtPackageManagerInterface>(m_dbusService,
+ u"/PackageManager"_s,
+ conn.name(), this);
+ if (!m_packager) {
+ throw Exception("Could not connect to the io.qt.PackageManager D-Bus interface on %1")
+ .arg(m_dbusName);
+ }
}
signals:
void disconnected(QString reason);
private:
+ template<typename T>
+ static T *tryConnectToDBusInterface(const QString &service, const QString &path,
+ const QString &connectionName, QObject *parent)
+ {
+ // we are working with very small delays in the milli-second range here, so a linear factor
+ // to support valgrind would have to be very large and probably conflict with usage elsewhere
+ // in the codebase, where the ranges are normally in the seconds.
+ static const int timeout = timeoutFactor() * timeoutFactor();
+
+ QDBusConnection conn(connectionName);
+
+ if (!conn.isConnected())
+ return nullptr;
+ if (!service.isEmpty() && conn.interface()) {
+ // the 'T' constructor can block up to 25sec (!), if the service is not registered!
+ if (!conn.interface()->isServiceRegistered(service))
+ return nullptr;
+ }
+
+ QElapsedTimer timer;
+ timer.start();
+
+ do {
+ T *iface = new T(service, path, conn, parent);
+ if (!iface->lastError().isValid())
+ return iface;
+ delete iface;
+ QThread::msleep(static_cast<unsigned long>(timeout));
+ } while (timer.elapsed() < (100 * timeout)); // 100msec base line
+
+ return nullptr;
+ }
+
QDBusConnection connectTo(const QString &iface) noexcept(false)
{
QDBusConnection conn(iface);
@@ -81,17 +129,23 @@ private:
QString dbus = m_dbusAddresses.value(iface).toString();
if (dbus == u"system") {
conn = QDBusConnection::systemBus();
- dbus = u"[system-bus]"_s;
+ m_dbusName = u"[system-bus]"_s;
} else if (dbus.isEmpty()) {
conn = QDBusConnection::sessionBus();
- dbus = u"[session-bus]"_s;
+ m_dbusName = u"[session-bus]"_s;
+ } else if (dbus.startsWith(u"p2p:")) {
+ const auto address = dbus.mid(4);
+ conn = QDBusConnection::connectToPeer(address, u"p2p"_s);
+ m_dbusName = u"[p2p] "_s + address;
+ m_dbusService.clear(); // no service names allowed on p2p busses
} else {
conn = QDBusConnection::connectToBus(dbus, u"custom"_s);
+ m_dbusName = dbus;
}
if (!conn.isConnected()) {
throw Exception(Error::IO, "Could not connect to the application manager D-Bus interface %1 at %2: %3")
- .arg(iface, dbus, conn.lastError().message());
+ .arg(iface, m_dbusName, conn.lastError().message());
}
installDisconnectWatcher(conn, u"io.qt.ApplicationManager"_s);
@@ -155,6 +209,8 @@ private:
IoQtPackageManagerInterface *m_packager = nullptr;
IoQtApplicationManagerInterface *m_manager = nullptr;
QVariantMap m_dbusAddresses;
+ QString m_dbusName;
+ QString m_dbusService;
QStringList m_connections;
QTimer *m_disconnectTimer = nullptr;
bool m_disconnectedEmitted = false;