diff options
author | Robert Griebl <robert.griebl@qt.io> | 2024-02-06 17:23:27 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-02-07 18:24:56 +0000 |
commit | 90c4a323203f6223c78539183d183effd11f232b (patch) | |
tree | d6f2adf2c9bb58058b86b1ed4a7fad5aabbb70d7 | |
parent | 563b91d2b310f4ef56909f50ce8eede9bed0b8e4 (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.cpp | 170 | ||||
-rw-r--r-- | src/main-lib/main.h | 10 | ||||
-rw-r--r-- | src/tools/controller/controller.cpp | 66 |
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; |