diff options
Diffstat (limited to 'src/dbus/qdbusintegrator.cpp')
-rw-r--r-- | src/dbus/qdbusintegrator.cpp | 242 |
1 files changed, 145 insertions, 97 deletions
diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 4d67875c75..1b03b7f6f6 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -11,7 +11,6 @@ #include <qobject.h> #include <qsocketnotifier.h> #include <qstringlist.h> -#include <qtimer.h> #include <qthread.h> #include <private/qlocking_p.h> #include <QtCore/qset.h> @@ -101,7 +100,34 @@ void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate * qdbusThreadDebugFunc qdbusThreadDebug = nullptr; #endif -typedef QVarLengthArray<QDBusSpyCallEvent::Hook, 4> QDBusSpyHookList; +class QDBusSpyHookList +{ +public: + void add(QDBusSpyCallEvent::Hook hook) + { + const auto locker = qt_scoped_lock(lock); + list.append(hook); + } + + void invoke(const QDBusMessage &msg) + { + // Create a copy of the hook list here, so that the hooks can be called + // without holding the lock. + QList<QDBusSpyCallEvent::Hook> hookListCopy; + { + const auto locker = qt_scoped_lock(lock); + hookListCopy = list; + } + + for (auto hook : std::as_const(hookListCopy)) + hook(msg); + } + +private: + QBasicMutex lock; + QList<QDBusSpyCallEvent::Hook> list; +}; + Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) extern "C" { @@ -124,8 +150,8 @@ static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) Q_ASSERT(d->timeouts.key(timeout, 0) == 0); - int timerId = d->startTimer(std::chrono::milliseconds{q_dbus_timeout_get_interval(timeout)}); - Q_ASSERT_X(timerId, "QDBusConnection", "Failed to start a timer"); + using namespace std::chrono_literals; + int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout) * 1ms); // no overflow possible if (!timerId) return false; @@ -251,7 +277,6 @@ static void qDBusToggleWatch(DBusWatch *watch, void *data) static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) { Q_ASSERT(connection); - Q_UNUSED(connection); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); if (new_status == DBUS_DISPATCH_DATA_REMAINS) emit d->dispatchStatusChanged(); @@ -261,11 +286,11 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v { // ### We may want to separate the server from the QDBusConnectionPrivate Q_ASSERT(server); - Q_UNUSED(server); Q_ASSERT(connection); Q_ASSERT(data); - if (!QDBusConnectionManager::instance()) + auto *manager = QDBusConnectionManager::instance(); + if (!manager) return; // keep the connection alive @@ -276,31 +301,35 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v if (serverConnection->anonymousAuthenticationAllowed) q_dbus_connection_set_allow_anonymous(connection, true); - QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent()); - const auto locker = qt_scoped_lock(QDBusConnectionManager::instance()->mutex); - QDBusConnectionManager::instance()->setConnection("QDBusServer-"_L1 + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), newConnection); - serverConnection->serverConnectionNames << newConnection->name; + QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate; + + manager->addConnection( + "QDBusServer-"_L1 + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), + newConnection); + { + QWriteLocker locker(&serverConnection->lock); + serverConnection->serverConnectionNames << newConnection->name; + } // setPeer does the error handling for us QDBusErrorInternal error; newConnection->setPeer(connection, error); newConnection->setDispatchEnabled(false); + QReadLocker serverLock(&serverConnection->lock); + if (!serverConnection->serverObject) + return; + // this is a queued connection and will resume in the QDBusServer's thread - emit serverConnection->newServerConnection(newConnection); + QMetaObject::invokeMethod(serverConnection->serverObject, &QDBusServer::newConnection, + Qt::QueuedConnection, QDBusConnectionPrivate::q(newConnection)); // we've disabled dispatching of events, so now we post an event to the // QDBusServer's thread in order to enable it after the // QDBusServer::newConnection() signal has been received by the // application's code - QReadLocker serverLock(&serverConnection->lock); - newConnection->enableDispatchDelayed(serverConnection->serverObject); -} -void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection) -{ - Q_ASSERT(mode == ServerMode); - emit serverObject->newConnection(QDBusConnectionPrivate::q(newConnection)); + newConnection->enableDispatchDelayed(serverConnection->serverObject); } } // extern "C" @@ -455,7 +484,11 @@ static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString & extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook); void qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook) { - qDBusSpyHookList()->append(hook); + auto *hooks = qDBusSpyHookList(); + if (!hooks) + return; + + hooks->add(hook); } QDBusSpyCallEvent::~QDBusSpyCallEvent() @@ -470,14 +503,15 @@ QDBusSpyCallEvent::~QDBusSpyCallEvent() void QDBusSpyCallEvent::placeMetaCall(QObject *) { - invokeSpyHooks(msg, hooks, hookCount); + invokeSpyHooks(msg); } -inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg, const Hook *hooks, int hookCount) +inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg) { - // call the spy hook list - for (int i = 0; i < hookCount; ++i) - hooks[i](msg); + if (!qDBusSpyHookList.exists()) + return; + + qDBusSpyHookList->invoke(msg); } extern "C" { @@ -526,15 +560,14 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) // a) if it's a local message, we're in the caller's thread, so invoke the filter directly // b) if it's an external message, post to the main thread if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) { - const QDBusSpyHookList &list = *qDBusSpyHookList; if (isLocal) { Q_ASSERT(QThread::currentThread() != thread()); qDBusDebug() << this << "invoking message spies directly"; - QDBusSpyCallEvent::invokeSpyHooks(amsg, list.constData(), list.size()); + QDBusSpyCallEvent::invokeSpyHooks(amsg); } else { qDBusDebug() << this << "invoking message spies via event"; - QCoreApplication::postEvent(qApp, new QDBusSpyCallEvent(this, QDBusConnection(this), - amsg, list.constData(), list.size())); + QCoreApplication::postEvent( + qApp, new QDBusSpyCallEvent(this, QDBusConnection(this), amsg)); // we'll be called back, so return return true; @@ -562,7 +595,7 @@ static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNo if (needle == haystack.obj) { haystack.obj = nullptr; - haystack.flags = 0; + haystack.flags = {}; } } @@ -573,7 +606,7 @@ static void huntAndUnregister(const QList<QStringView> &pathComponents, int i, if (pathComponents.size() == i) { // found it node->obj = nullptr; - node->flags = 0; + node->flags = {}; if (mode == QDBusConnection::UnregisterTree) { // clear the sub-tree as well @@ -632,6 +665,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, const QString &signature_, QList<QMetaType> &metaTypes) { QByteArray msgSignature = signature_.toLatin1(); + QString parametersErrorMsg; for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { QMetaMethod mm = mo->method(idx); @@ -658,8 +692,10 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, QString errorMsg; int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg); - if (inputCount == -1) + if (inputCount == -1) { + parametersErrorMsg = errorMsg; continue; // problem parsing + } metaTypes[0] = returnType; bool hasMessage = false; @@ -721,6 +757,13 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, } // no slot matched + if (!parametersErrorMsg.isEmpty()) { + qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s: %ls", + name.constData(), qUtf16Printable(parametersErrorMsg)); + } else { + qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s, no slot matched", + name.constData()); + } return -1; } @@ -746,7 +789,6 @@ QDBusCallDeliveryEvent *QDBusConnectionPrivate::prepareReply(QDBusConnectionPriv const QDBusMessage &msg) { Q_ASSERT(object); - Q_UNUSED(object); int n = metaTypes.size() - 1; if (metaTypes[n] == QDBusMetaTypeId::message()) @@ -782,14 +824,15 @@ void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::Signal if (call == DIRECT_DELIVERY) { // short-circuit delivery Q_ASSERT(this == hook.obj); - deliverCall(this, 0, msg, hook.params, hook.midx); + deliverCall(this, msg, hook.params, hook.midx); return; } if (call) postEventToThread(ActivateSignalAction, hook.obj, call); } -bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) +bool QDBusConnectionPrivate::activateCall(QObject *object, QDBusConnection::RegisterOptions flags, + const QDBusMessage &msg) { // This is called by QDBusConnectionPrivate::handleObjectCall to place a call // to a slot on the object. @@ -829,19 +872,15 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBu cacheKey += signature; } - QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey); - while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags && - cacheIt.key() == cacheKey) - ++cacheIt; - if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey) - { + QDBusSlotCache::Key compoundKey{ std::move(cacheKey), flags }; + QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(compoundKey); + if (cacheIt == slotCache.hash.constEnd()) { // not cached, analyze the meta object const QMetaObject *mo = object->metaObject(); QByteArray memberName = msg.member().toUtf8(); // find a slot that matches according to the rules above QDBusSlotCache::Data slotData; - slotData.flags = flags; slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes); if (slotData.slotIdx == -1) { // ### this is where we want to add the connection as an arg too @@ -853,7 +892,7 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBu // save the negative lookup slotData.slotIdx = -1; slotData.metaTypes.clear(); - slotCache.hash.insert(cacheKey, slotData); + slotCache.hash.insert(compoundKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); qCWarning(dbusIntegration).nospace() << "Could not find slot " << mo->className() @@ -863,24 +902,24 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBu } // save to the cache - slotCache.hash.insert(cacheKey, slotData); + slotCache.hash.insert(compoundKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); // found the slot to be called - deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx); + deliverCall(object, msg, slotData.metaTypes, slotData.slotIdx); return true; } else if (cacheIt->slotIdx == -1) { // negative cache return false; } else { // use the cache - deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx); + deliverCall(object, msg, cacheIt->metaTypes, cacheIt->slotIdx); return true; } return false; } -void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, +void QDBusConnectionPrivate::deliverCall(QObject *object, const QDBusMessage &msg, const QList<QMetaType> &metaTypes, int slotIdx) { Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), @@ -989,9 +1028,8 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q return; } -QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) - : QObject(p), - ref(1), +QDBusConnectionPrivate::QDBusConnectionPrivate() + : ref(1), mode(InvalidMode), busService(nullptr), connection(nullptr), @@ -1017,12 +1055,8 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) this, &QDBusConnectionPrivate::handleObjectCall, Qt::QueuedConnection); connect(this, &QDBusConnectionPrivate::messageNeedsSending, this, &QDBusConnectionPrivate::sendInternal); - connect(this, &QDBusConnectionPrivate::signalNeedsConnecting, - this, &QDBusConnectionPrivate::addSignalHook, Qt::BlockingQueuedConnection); - connect(this, &QDBusConnectionPrivate::signalNeedsDisconnecting, - this, &QDBusConnectionPrivate::removeSignalHook, Qt::BlockingQueuedConnection); - rootNode.flags = 0; + rootNode.flags = {}; // prepopulate watchedServices: // we know that the owner of org.freedesktop.DBus is itself @@ -1258,7 +1292,6 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name, const QString &oldOwner, const QString &newOwner) { - Q_UNUSED(oldOwner); // QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this); WatchedServicesHash::Iterator it = watchedServices.find(name); if (it == watchedServices.end()) @@ -1288,14 +1321,13 @@ int QDBusConnectionPrivate::findSlot(QObject *obj, const QByteArray &normalizedN } bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, - const QString &service, - const QString &path, const QString &interface, const QString &name, - const ArgMatchRules &argMatch, - QObject *receiver, const char *signal, int minMIdx, - bool buildSignature) + const QString &service, const QString &path, + const QString &interface, const QString &name, + const ArgMatchRules &argMatch, QObject *receiver, + const char *signal, int minMIdx, bool buildSignature, + QString &errorMsg) { QByteArray normalizedName = signal + 1; - QString errorMsg; hook.midx = findSlot(receiver, signal + 1, hook.params, errorMsg); if (hook.midx == -1) { normalizedName = QMetaObject::normalizedSignature(signal + 1); @@ -1433,7 +1465,7 @@ void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMes QDBusAdaptorConnector *connector; if (node.flags & QDBusConnection::ExportAdaptors && (connector = qDBusFindAdaptorConnector(node.obj))) { - int newflags = node.flags | QDBusConnection::ExportAllSlots; + auto newflags = node.flags | QDBusConnection::ExportAllSlots; if (msg.interface().isEmpty()) { // place the call in all interfaces @@ -1640,7 +1672,7 @@ void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) key += u':'; key += msg.interface(); - QDBusReadLocker locker(HandleSignalAction, this); + QDBusWriteLocker locker(HandleSignalAction, this); handleSignal(key, msg); // one try key.truncate(msg.member().size() + 1); // keep the ':' @@ -1827,7 +1859,6 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) { QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data); Q_ASSERT(call->pending == pending); - Q_UNUSED(pending); QDBusConnectionPrivate::processFinishedCall(call); } } @@ -1944,7 +1975,7 @@ bool QDBusConnectionPrivate::send(const QDBusMessage& message) class QDBusBlockingCallWatcher { public: - QDBusBlockingCallWatcher(const QDBusMessage &message) + Q_NODISCARD_CTOR QDBusBlockingCallWatcher(const QDBusMessage &message) : m_message(message), m_maxCallTimeoutMs(0) { #if defined(QT_NO_DEBUG) @@ -2027,29 +2058,18 @@ private: QElapsedTimer m_callTimer; }; - QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, - int sendMode, int timeout) + QDBus::CallMode mode, int timeout) { QDBusBlockingCallWatcher watcher(message); QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, nullptr, nullptr, nullptr, timeout); Q_ASSERT(pcall); - if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { - // need to wait for the reply - if (sendMode == QDBus::BlockWithGui) { - pcall->watcherHelper = new QDBusPendingCallWatcherHelper; - QEventLoop loop; - loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::reply, &loop, &QEventLoop::quit); - loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::error, &loop, &QEventLoop::quit); - - // enter the event loop and wait for a reply - loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); - } else { - pcall->waitForFinished(); - } - } + if (mode == QDBus::BlockWithGui) + pcall->waitForFinishedWithGui(); + else + pcall->waitForFinished(); QDBusMessage reply = pcall->replyMessage; lastError = QDBusError(reply); // set or clear error @@ -2110,6 +2130,7 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM pcall->setReplyCallback(receiver, returnMethod); if (errorMethod) { + Q_ASSERT(!pcall->watcherHelper); pcall->watcherHelper = new QDBusPendingCallWatcherHelper; connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod, Qt::QueuedConnection); @@ -2211,18 +2232,30 @@ bool QDBusConnectionPrivate::connectSignal(const QString &service, QString key; hook.signature = signature; + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, - false)) { - qCWarning(dbusIntegration) << "Could not connect" << interface << "to" << slot + 1; + false, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not connect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg); return false; // don't connect } Q_ASSERT(thread() != QThread::currentThread()); - return emit signalNeedsConnecting(key, hook); + return addSignalHook(key, hook); } bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook &hook) { + bool result = false; + + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::addSignalHookImpl, + Qt::BlockingQueuedConnection, qReturnArg(result), key, hook); + + return result; +} + +bool QDBusConnectionPrivate::addSignalHookImpl(const QString &key, const SignalHook &hook) +{ QDBusWriteLocker locker(ConnectAction, this); // avoid duplicating: @@ -2304,18 +2337,30 @@ bool QDBusConnectionPrivate::disconnectSignal(const QString &service, name2.detach(); hook.signature = signature; + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, - false)) { - qCWarning(dbusIntegration) << "Could not disconnect" << interface << "to" << slot + 1; + false, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not disconnect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg); return false; // don't disconnect } Q_ASSERT(thread() != QThread::currentThread()); - return emit signalNeedsDisconnecting(key, hook); + return removeSignalHook(key, hook); } bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHook &hook) { + bool result = false; + + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::removeSignalHookImpl, + Qt::BlockingQueuedConnection, qReturnArg(result), key, hook); + + return result; +} + +bool QDBusConnectionPrivate::removeSignalHookImpl(const QString &key, const SignalHook &hook) +{ // remove it from our list: QDBusWriteLocker locker(ConnectAction, this); QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key); @@ -2436,14 +2481,16 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, - QDBusAbstractInterface::staticMetaObject.methodCount(), true)) { - qCWarning(dbusIntegration) << "Could not connect" << interface << "to" << signal.name(); + QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not connect" << interface << "to" << signal.name() << ":" << qPrintable(errorMsg); return; // don't connect } Q_ASSERT(thread() != QThread::currentThread()); - emit signalNeedsConnecting(key, hook); + addSignalHook(key, hook); } void QDBusConnectionPrivate::disconnectRelay(const QString &service, @@ -2459,15 +2506,16 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, - QDBusAbstractInterface::staticMetaObject.methodCount(), true)) { - qCWarning(dbusIntegration) - << "Could not disconnect" << interface << "to" << signal.methodSignature(); + QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) { + qCWarning(dbusIntegration) << "Could not disconnect" << interface << "to" + << signal.methodSignature() << ":" << qPrintable(errorMsg); return; // don't disconnect } Q_ASSERT(thread() != QThread::currentThread()); - emit signalNeedsDisconnecting(key, hook); + removeSignalHook(key, hook); } bool QDBusConnectionPrivate::shouldWatchService(const QString &service) @@ -2666,8 +2714,8 @@ void QDBusConnectionPrivate::enableDispatchDelayed(QObject *context) // This call cannot race with something disabling dispatch only // because dispatch is never re-disabled from Qt code on an // in-use connection once it has been enabled. - QMetaObject::invokeMethod( - this, [this] { setDispatchEnabled(true); }, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::setDispatchEnabled, + Qt::QueuedConnection, true); if (!ref.deref()) deleteLater(); }, |