qdbusintegrator.cpp 96.17 KiB
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtDBus module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
** $QT_END_LICENSE$
****************************************************************************/
#include "qdbusintegrator_p.h"
#include <qcoreapplication.h>
#include <qelapsedtimer.h>
#include <qdebug.h>
#include <qmetaobject.h>
#include <qobject.h>
#include <qsocketnotifier.h>
#include <qstringlist.h>
#include <qtimer.h>
#include <qthread.h>
#include "qdbusargument.h"
#include "qdbusconnection_p.h"
#include "qdbusconnectionmanager_p.h"
#include "qdbusinterface_p.h"
#include "qdbusmessage.h"
#include "qdbusmetatype.h"
#include "qdbusmetatype_p.h"
#include "qdbusabstractadaptor.h"
#include "qdbusabstractadaptor_p.h"
#include "qdbusutil_p.h"
#include "qdbusvirtualobject.h"
#include "qdbusmessage_p.h"
#include "qdbuscontext_p.h"
#include "qdbuspendingcall_p.h"
#include "qdbusthreaddebug_p.h"
#include <algorithm>
#ifndef QT_NO_DBUS
QT_BEGIN_NAMESPACE
static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1);
#define qDBusDebug              if (::isDebugging == 0); else qDebug
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
static inline QString orgFreedesktopDBusString() { return QStringLiteral(DBUS_SERVICE_DBUS); } static inline QString dbusServiceString() { return orgFreedesktopDBusString(); } static inline QString dbusInterfaceString() { // it's the same string, but just be sure Q_ASSERT(orgFreedesktopDBusString() == QLatin1String(DBUS_INTERFACE_DBUS)); return orgFreedesktopDBusString(); } static inline QDebug operator<<(QDebug dbg, const QThread *th) { dbg.nospace() << "QThread(ptr=" << (void*)th; if (th && !th->objectName().isEmpty()) dbg.nospace() << ", name=" << th->objectName(); dbg.nospace() << ')'; return dbg.space(); } #if QDBUS_THREAD_DEBUG static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn) { dbg.nospace() << "QDBusConnection(" << "ptr=" << (void*)conn << ", name=" << conn->name << ", baseService=" << conn->baseService << ", thread="; if (conn->thread() == QThread::currentThread()) dbg.nospace() << "same thread"; else dbg.nospace() << conn->thread(); dbg.nospace() << ')'; return dbg.space(); } void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn) { qDBusDebug() << QThread::currentThread() << "Qt D-Bus threading action" << action << (condition == QDBusLockerBase::BeforeLock ? "before lock" : condition == QDBusLockerBase::AfterLock ? "after lock" : condition == QDBusLockerBase::BeforeUnlock ? "before unlock" : condition == QDBusLockerBase::AfterUnlock ? "after unlock" : condition == QDBusLockerBase::BeforePost ? "before event posting" : condition == QDBusLockerBase::AfterPost ? "after event posting" : condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" : condition == QDBusLockerBase::AfterDeliver ? "after event delivery" : condition == QDBusLockerBase::BeforeAcquire ? "before acquire" : condition == QDBusLockerBase::AfterAcquire ? "after acquire" : condition == QDBusLockerBase::BeforeRelease ? "before release" : condition == QDBusLockerBase::AfterRelease ? "after release" : "condition unknown") << "in connection" << conn; } qdbusThreadDebugFunc qdbusThreadDebug = 0; #endif typedef void (*QDBusSpyHook)(const QDBusMessage&); typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList; Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) extern "C" {
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
// libdbus-1 callbacks static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms); static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) { Q_ASSERT(timeout); Q_ASSERT(data); // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout)); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); if (!q_dbus_timeout_get_enabled(timeout)) return true; QDBusDispatchLocker locker(AddTimeoutAction, d); if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { // correct thread return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout)); } else { // wrong thread: sync back QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; ev->subtype = QDBusConnectionCallbackEvent::AddTimeout; d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout))); d->postEventToThread(AddTimeoutAction, d, ev); return true; } } static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms) { Q_ASSERT(d->timeouts.keys(timeout).isEmpty()); int timerId = d->startTimer(ms); if (!timerId) return false; d->timeouts[timerId] = timeout; return true; } static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) { Q_ASSERT(timeout); Q_ASSERT(data); // qDebug("removeTimeout"); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); QDBusDispatchLocker locker(RemoveTimeoutAction, d); // is it pending addition? QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin(); while (pit != d->timeoutsPendingAdd.end()) { if (pit->first == timeout) pit = d->timeoutsPendingAdd.erase(pit); else ++pit; } // is it a running timer? bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread(); QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); while (it != d->timeouts.end()) { if (it.value() == timeout) { if (correctThread) { // correct thread d->killTimer(it.key());
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
} else { // incorrect thread or no application, post an event for later QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; ev->subtype = QDBusConnectionCallbackEvent::KillTimer; ev->timerId = it.key(); d->postEventToThread(KillTimerAction, d, ev); } it = d->timeouts.erase(it); break; } else { ++it; } } } static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) { Q_ASSERT(timeout); Q_ASSERT(data); //qDebug("ToggleTimeout"); qDBusRemoveTimeout(timeout, data); qDBusAddTimeout(timeout, data); } static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd); static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) { Q_ASSERT(watch); Q_ASSERT(data); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); int flags = q_dbus_watch_get_flags(watch); int fd = q_dbus_watch_get_unix_fd(watch); if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { return qDBusRealAddWatch(d, watch, flags, fd); } else { QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; ev->subtype = QDBusConnectionCallbackEvent::AddWatch; ev->watch = watch; ev->fd = fd; ev->extra = flags; d->postEventToThread(AddWatchAction, d, ev); return true; } } static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd) { QDBusConnectionPrivate::Watcher watcher; QDBusDispatchLocker locker(AddWatchAction, d); if (flags & DBUS_WATCH_READABLE) { //qDebug("addReadWatch %d", fd); watcher.watch = watch; if (QCoreApplication::instance()) { watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); } } if (flags & DBUS_WATCH_WRITABLE) { //qDebug("addWriteWatch %d", fd); watcher.watch = watch; if (QCoreApplication::instance()) { watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); watcher.write->setEnabled(q_dbus_watch_get_enabled(watch));
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); } } d->watchers.insertMulti(fd, watcher); return true; } static void qDBusRemoveWatch(DBusWatch *watch, void *data) { Q_ASSERT(watch); Q_ASSERT(data); //qDebug("remove watch"); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); int fd = q_dbus_watch_get_unix_fd(watch); QDBusDispatchLocker locker(RemoveWatchAction, d); QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); while (i != d->watchers.end() && i.key() == fd) { if (i.value().watch == watch) { if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { // correct thread, delete the socket notifiers delete i.value().read; delete i.value().write; } else { // incorrect thread or no application, use delete later if (i->read) i->read->deleteLater(); if (i->write) i->write->deleteLater(); } i = d->watchers.erase(i); } else { ++i; } } } static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd); static void qDBusToggleWatch(DBusWatch *watch, void *data) { Q_ASSERT(watch); Q_ASSERT(data); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); int fd = q_dbus_watch_get_unix_fd(watch); if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { qDBusRealToggleWatch(d, watch, fd); } else { QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch; ev->watch = watch; ev->fd = fd; d->postEventToThread(ToggleWatchAction, d, ev); } } static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd) { QDBusDispatchLocker locker(ToggleWatchAction, d); QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); while (i != d->watchers.end() && i.key() == fd) { if (i.value().watch == watch) { bool enabled = q_dbus_watch_get_enabled(watch); int flags = q_dbus_watch_get_flags(watch);
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
//qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE); if (flags & DBUS_WATCH_READABLE && i.value().read) i.value().read->setEnabled(enabled); if (flags & DBUS_WATCH_WRITABLE && i.value().write) i.value().write->setEnabled(enabled); return; } ++i; } } static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) { Q_ASSERT(connection); Q_UNUSED(connection); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); static int slotId; // 0 is QObject::deleteLater() if (!slotId) { // it's ok to do this: there's no race condition because the store is atomic // and we always set to the same value slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()"); } //qDBusDebug() << "Updating dispatcher status" << slotId; if (new_status == DBUS_DISPATCH_DATA_REMAINS) QDBusConnectionPrivate::staticMetaObject.method(slotId). invoke(d, Qt::QueuedConnection); } static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data) { // ### We may want to separate the server from the QDBusConnectionPrivate Q_ASSERT(server); Q_UNUSED(server); Q_ASSERT(connection); Q_ASSERT(data); // keep the connection alive q_dbus_connection_ref(connection); QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data); // allow anonymous authentication if (serverConnection->anonymousAuthenticationAllowed) q_dbus_connection_set_allow_anonymous(connection, true); QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent()); QMutexLocker locker(&QDBusConnectionManager::instance()->mutex); QDBusConnectionManager::instance()->setConnection(QLatin1String("QDBusServer-") + QString::number(reinterpret_cast<qulonglong>(newConnection)), newConnection); serverConnection->serverConnectionNames << newConnection->name; // setPeer does the error handling for us QDBusErrorInternal error; newConnection->setPeer(connection, error); QDBusConnection retval = QDBusConnectionPrivate::q(newConnection); // make QDBusServer emit the newConnection signal serverConnection->serverConnection(retval); } } // extern "C" static QByteArray buildMatchRule(const QString &service, const QString &objectPath, const QString &interface, const QString &member, const QStringList &argMatch, const QString & /*signature*/) { QString result = QLatin1String("type='signal',"); QString keyValue = QLatin1String("%1='%2',");
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
if (!service.isEmpty()) result += keyValue.arg(QLatin1String("sender"), service); if (!objectPath.isEmpty()) result += keyValue.arg(QLatin1String("path"), objectPath); if (!interface.isEmpty()) result += keyValue.arg(QLatin1String("interface"), interface); if (!member.isEmpty()) result += keyValue.arg(QLatin1String("member"), member); // add the argument string-matching now if (!argMatch.isEmpty()) { keyValue = QLatin1String("arg%1='%2',"); for (int i = 0; i < argMatch.count(); ++i) if (!argMatch.at(i).isNull()) result += keyValue.arg(i).arg(argMatch.at(i)); } result.chop(1); // remove ending comma return result.toLatin1(); } static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, int &usedLength, QDBusConnectionPrivate::ObjectTreeNode &result) { if (!fullpath.compare(QLatin1String("/")) && root->obj) { usedLength = 1; result = *root; return root; } int start = 0; int length = fullpath.length(); if (fullpath.at(0) == QLatin1Char('/')) start = 1; // walk the object tree QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root; while (start < length && node) { if (node->flags & QDBusConnection::ExportChildObjects) break; if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) break; int end = fullpath.indexOf(QLatin1Char('/'), start); end = (end == -1 ? length : end); QStringRef pathComponent(&fullpath, start, end - start); QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent); if (it != node->children.constEnd() && it->name == pathComponent) // match node = it; else node = 0; start = end + 1; } // found our object usedLength = (start > length ? length : start); if (node) { if (node->obj || !node->children.isEmpty()) result = *node; else // there really is no object here // we're just looking at an unused space in the QVector node = 0; } return node; }
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, int start) { int length = fullpath.length(); // any object in the tree can tell us to switch to its own object tree: const QDBusConnectionPrivate::ObjectTreeNode *node = root; if (node && node->flags & QDBusConnection::ExportChildObjects) { QObject *obj = node->obj; while (obj) { if (start >= length) // we're at the correct level return obj; int pos = fullpath.indexOf(QLatin1Char('/'), start); pos = (pos == -1 ? length : pos); QStringRef pathComponent(&fullpath, start, pos - start); const QObjectList children = obj->children(); // find a child with the proper name QObject *next = 0; QObjectList::ConstIterator it = children.constBegin(); QObjectList::ConstIterator end = children.constEnd(); for ( ; it != end; ++it) if ((*it)->objectName() == pathComponent) { next = *it; break; } if (!next) break; obj = next; start = pos + 1; } } // object not found return 0; } static bool shouldWatchService(const QString &service) { return !service.isEmpty() && !service.startsWith(QLatin1Char(':')); } extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); void qDBusAddSpyHook(QDBusSpyHook hook) { qDBusSpyHookList()->append(hook); } extern "C" { static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) { Q_ASSERT(data); Q_UNUSED(connection); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); if (d->mode == QDBusConnectionPrivate::InvalidMode) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->capabilities); qDBusDebug() << d << "got message (signal):" << amsg; return d->handleMessage(amsg) ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
} } bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) { const QDBusSpyHookList *list = qDBusSpyHookList(); for (int i = 0; i < list->size(); ++i) { qDBusDebug() << "calling the message spy hook"; (*(*list)[i])(amsg); } if (!ref.load()) return false; switch (amsg.type()) { case QDBusMessage::SignalMessage: handleSignal(amsg); // if there are any other filters in this DBusConnection, // let them see the signal too return false; case QDBusMessage::MethodCallMessage: handleObjectCall(amsg); return true; case QDBusMessage::ReplyMessage: case QDBusMessage::ErrorMessage: case QDBusMessage::InvalidMessage: return false; // we don't handle those here } return false; } static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack) { QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin(); while (it != haystack.children.end()) { huntAndDestroy(needle, *it); if (!it->isActive()) it = haystack.children.erase(it); else it++; } if (needle == haystack.obj) { haystack.obj = 0; haystack.flags = 0; } } static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusConnection::UnregisterMode mode, QDBusConnectionPrivate::ObjectTreeNode *node) { if (pathComponents.count() == i) { // found it node->obj = 0; node->flags = 0; if (mode == QDBusConnection::UnregisterTree) { // clear the sub-tree as well node->children.clear(); // can't disconnect the objects because we really don't know if they can // be found somewhere else in the path too } } else { // keep going QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end(); QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = std::lower_bound(node->children.begin(), end, pathComponents.at(i)); if (it == end || it->name != pathComponents.at(i)) return; // node not found
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
huntAndUnregister(pathComponents, i + 1, mode, it); if (!it->isActive()) node->children.erase(it); } } static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, bool isScriptable, bool isAdaptor, const QString &path = QString()) { QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin(); QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd(); for ( ; it != end; ++it) { if (it->isActive()) huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name); } if (needle == haystack.obj) { // is this a signal we should relay? if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0) return; // no: it comes from an adaptor and we're not exporting adaptors else if (!isAdaptor) { int mask = isScriptable ? QDBusConnection::ExportScriptableSignals : QDBusConnection::ExportNonScriptableSignals; if ((haystack.flags & mask) == 0) return; // signal was not exported } QByteArray p = path.toLatin1(); if (p.isEmpty()) p = "/"; qDBusDebug() << QThread::currentThread() << "emitting signal at" << p; DBusMessage *msg2 = q_dbus_message_copy(msg); q_dbus_message_set_path(msg2, p); q_dbus_connection_send(connection, msg2, 0); q_dbus_message_unref(msg2); } } static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, const QString &signature_, QVector<int> &metaTypes) { QByteArray msgSignature = signature_.toLatin1(); for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { QMetaMethod mm = mo->method(idx); // check access: if (mm.access() != QMetaMethod::Public) continue; // check type: if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method) continue; // check name: if (mm.name() != name) continue; int returnType = mm.returnType(); bool isAsync = qDBusCheckAsyncTag(mm.tag()); bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; // consistency check: if (isAsync && returnType != QMetaType::Void) continue; QString errorMsg;
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg); if (inputCount == -1) continue; // problem parsing metaTypes[0] = returnType; bool hasMessage = false; if (inputCount > 0 && metaTypes.at(inputCount) == QDBusMetaTypeId::message()) { // "no input parameters" is allowed as long as the message meta type is there hasMessage = true; --inputCount; } // try to match the parameters int i; QByteArray reconstructedSignature; for (i = 1; i <= inputCount; ++i) { const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) ); if (!typeSignature) break; // invalid reconstructedSignature += typeSignature; if (!msgSignature.startsWith(reconstructedSignature)) break; } if (reconstructedSignature != msgSignature) continue; // we didn't match them all if (hasMessage) ++i; // make sure that the output parameters have signatures too if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == 0) continue; bool ok = true; for (int j = i; ok && j < metaTypes.count(); ++j) if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0) ok = false; if (!ok) continue; // consistency check: if (isAsync && metaTypes.count() > i + 1) continue; if (mm.methodType() == QMetaMethod::Slot) { if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0) continue; // scriptable slots not exported if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0) continue; // non-scriptable slots not exported } else { if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0) continue; // scriptable invokables not exported if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0) continue; // non-scriptable invokables not exported } // if we got here, this slot matched return idx; } // no slot matched return -1; } static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1; QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
QObject *object, int idx, const QVector<int> &metaTypes, const QDBusMessage &msg) { Q_ASSERT(object); Q_UNUSED(object); int n = metaTypes.count() - 1; if (metaTypes[n] == QDBusMetaTypeId::message()) --n; if (msg.arguments().count() < n) return 0; // too few arguments // check that types match for (int i = 0; i < n; ++i) if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() && msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>()) return 0; // no match // we can deliver // prepare for the call if (target == object) return DIRECT_DELIVERY; return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes); } void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook, const QDBusMessage &msg) { // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal // that was received from D-Bus // // Signals are delivered to slots if the parameters match // Slots can have less parameters than there are on the message // Slots can optionally have one final parameter that is a QDBusMessage // Slots receive read-only copies of the message (i.e., pass by value or by const-ref) QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg); if (call == DIRECT_DELIVERY) { // short-circuit delivery Q_ASSERT(this == hook.obj); deliverCall(this, 0, msg, hook.params, hook.midx); return; } if (call) postEventToThread(ActivateSignalAction, hook.obj, call); } bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) { // This is called by QDBusConnectionPrivate::handleObjectCall to place a call // to a slot on the object. // // The call is delivered to the first slot that matches the following conditions: // - has the same name as the message's target member // - ALL of the message's types are found in slot's parameter list // - optionally has one more parameter of type QDBusMessage // If none match, then the slot of the same name as the message target and with // the first type of QDBusMessage is delivered. // // The D-Bus specification requires that all MethodCall messages be replied to, unless the // caller specifically waived this requirement. This means that we inspect if the user slot // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a // QDBusMessage parameter, it cannot generate a reply. // // When a return message is generated, the slot's return type, if any, will be placed // in the message's first position. If there are non-const reference parameters to the // slot, they must appear at the end and will be placed in the subsequent message // positions.
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
static const char cachePropertyName[] = "_qdbus_slotCache"; if (!object) return false; #ifndef QT_NO_PROPERTIES Q_ASSERT_X(QThread::currentThread() == object->thread(), "QDBusConnection: internal threading error", "function called for an object that is in another thread!!"); QDBusSlotCache slotCache = qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName)); QString cacheKey = msg.member(), signature = msg.signature(); if (!signature.isEmpty()) { cacheKey.reserve(cacheKey.length() + 1 + signature.length()); cacheKey += QLatin1Char('.'); 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) { // 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 // try with no parameters, but with a QDBusMessage slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes); if (slotData.metaTypes.count() != 2 || slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) { // not found // save the negative lookup slotData.slotIdx = -1; slotData.metaTypes.clear(); slotCache.hash.insert(cacheKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); return false; } } // save to the cache slotCache.hash.insert(cacheKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); // found the slot to be called deliverCall(object, flags, 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); return true; } #endif // QT_NO_PROPERTIES return false; } void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, const QVector<int> &metaTypes, int slotIdx)
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
{ Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), "QDBusConnection: internal threading error", "function called for an object that is in another thread!!"); QVarLengthArray<void *, 10> params; params.reserve(metaTypes.count()); QVariantList auxParameters; // let's create the parameter list // first one is the return type -- add it below params.append(0); // add the input parameters int i; int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1); for (i = 1; i <= pCount; ++i) { int id = metaTypes[i]; if (id == QDBusMetaTypeId::message()) break; const QVariant &arg = msg.arguments().at(i - 1); if (arg.userType() == id) // no conversion needed params.append(const_cast<void *>(arg.constData())); else if (arg.userType() == qMetaTypeId<QDBusArgument>()) { // convert to what the function expects void *null = 0; auxParameters.append(QVariant(id, null)); const QDBusArgument &in = *reinterpret_cast<const QDBusArgument *>(arg.constData()); QVariant &out = auxParameters[auxParameters.count() - 1]; if (!QDBusMetaType::demarshall(in, out.userType(), out.data())) qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", out.typeName(), out.userType()); params.append(const_cast<void *>(out.constData())); } else { qFatal("Internal error: got invalid meta type %d (%s) " "when trying to convert to meta type %d (%s)", arg.userType(), QMetaType::typeName(arg.userType()), id, QMetaType::typeName(id)); } } if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) { params.append(const_cast<void*>(static_cast<const void*>(&msg))); ++i; } // output arguments QVariantList outputArgs; void *null = 0; if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) { QVariant arg(metaTypes[0], null); outputArgs.append( arg ); params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); } for ( ; i < metaTypes.count(); ++i) { QVariant arg(metaTypes[i], null); outputArgs.append( arg ); params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData())); } // make call: bool fail; if (!object) {
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
fail = true; } else { // FIXME: save the old sender! QDBusContextPrivate context(QDBusConnection(this), msg); QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context); QDBusConnectionPrivate::setSender(this); QPointer<QObject> ptr = object; fail = object->qt_metacall(QMetaObject::InvokeMetaMethod, slotIdx, params.data()) >= 0; QDBusConnectionPrivate::setSender(0); // the object might be deleted in the slot if (!ptr.isNull()) QDBusContextPrivate::set(object, old); } // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent // yet. if (msg.isReplyRequired() && !msg.isDelayedReply()) { if (!fail) { // normal reply qDBusDebug() << this << "Automatically sending reply:" << outputArgs; send(msg.createReply(outputArgs)); } else { // generate internal error qWarning("Internal error: Failed to deliver message"); send(msg.createErrorReply(QDBusError::InternalError, QLatin1String("Failed to deliver message"))); } } return; } extern bool qDBusInitThreads(); QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) : QObject(p), ref(1), capabilities(0), mode(InvalidMode), busService(0), dispatchLock(QMutex::Recursive), connection(0), server(0), rootNode(QString(QLatin1Char('/'))), anonymousAuthenticationAllowed(false) { static const bool threads = q_dbus_threads_init_default(); if (::isDebugging == -1) ::isDebugging = qgetenv("QDBUS_DEBUG").toInt(); Q_UNUSED(threads) #ifdef QDBUS_THREAD_DEBUG if (::isDebugging > 1) qdbusThreadDebug = qdbusDefaultThreadDebug; #endif QDBusMetaTypeId::init(); rootNode.flags = 0; // prepopulate watchedServices: // we know that the owner of org.freedesktop.DBus is itself watchedServices.insert(dbusServiceString(), WatchedServiceData(dbusServiceString(), 1)); // prepopulate matchRefCounts: // we know that org.freedesktop.DBus will never change owners matchRefCounts.insert("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", 1); } QDBusConnectionPrivate::~QDBusConnectionPrivate() { if (thread() && thread() != QThread::currentThread()) qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " "Timer and socket errors will follow and the program will probably crash",
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
qPrintable(name)); closeConnection(); rootNode.children.clear(); // free resources qDeleteAll(cachedMetaObjects); if (server) q_dbus_server_unref(server); if (connection) q_dbus_connection_unref(connection); connection = 0; server = 0; } void QDBusConnectionPrivate::deleteYourself() { if (thread() && thread() != QThread::currentThread()) { // last reference dropped while not in the correct thread // ask the correct thread to delete // note: since we're posting an event to another thread, we // must consider deleteLater() to take effect immediately deleteLater(); } else { delete this; } } void QDBusConnectionPrivate::closeConnection() { QDBusWriteLocker locker(CloseConnectionAction, this); ConnectionMode oldMode = mode; mode = InvalidMode; // prevent reentrancy baseService.clear(); if (server) q_dbus_server_disconnect(server); if (oldMode == ClientMode || oldMode == PeerMode) { if (connection) { q_dbus_connection_close(connection); // send the "close" message while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; } } qDeleteAll(pendingCalls); qDBusDebug() << this << "Disconnected"; } void QDBusConnectionPrivate::checkThread() { if (!thread()) { if (QCoreApplication::instance()) moveToThread(QCoreApplication::instance()->thread()); else qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread", qPrintable(name)); } } bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error) { if (!error) return false; // no error //lock.lockForWrite();
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
lastError = error; //lock.unlock(); return true; } void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) { { QDBusDispatchLocker locker(TimerEventAction, this); DBusTimeout *timeout = timeouts.value(e->timerId(), 0); if (timeout) q_dbus_timeout_handle(timeout); } doDispatch(); } void QDBusConnectionPrivate::customEvent(QEvent *e) { Q_ASSERT(e->type() == QEvent::User); QDBusConnectionCallbackEvent *ev = static_cast<QDBusConnectionCallbackEvent *>(e); QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), QDBusLockerBase::BeforeDeliver, this); switch (ev->subtype) { case QDBusConnectionCallbackEvent::AddTimeout: { QDBusDispatchLocker locker(RealAddTimeoutAction, this); while (!timeoutsPendingAdd.isEmpty()) { QPair<DBusTimeout *, int> entry = timeoutsPendingAdd.takeFirst(); qDBusRealAddTimeout(this, entry.first, entry.second); } break; } case QDBusConnectionCallbackEvent::KillTimer: killTimer(ev->timerId); break; case QDBusConnectionCallbackEvent::AddWatch: qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd); break; case QDBusConnectionCallbackEvent::ToggleWatch: qDBusRealToggleWatch(this, ev->watch, ev->fd); break; } QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), QDBusLockerBase::AfterDeliver, this); } void QDBusConnectionPrivate::doDispatch() { QDBusDispatchLocker locker(DoDispatchAction, this); if (mode == ClientMode || mode == PeerMode) while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; } void QDBusConnectionPrivate::socketRead(int fd) { QDBusDispatchLocker locker(SocketReadAction, this); WatcherHash::ConstIterator it = watchers.constFind(fd); while (it != watchers.constEnd() && it.key() == fd) { if (it->watch && it->read && it->read->isEnabled()) { if (!q_dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE)) qDebug("OUT OF MEM"); } ++it; } doDispatch();