An error occurred while loading the file. Please try again.
-
Oswald Buddenhagen authored
Conflicts: src/plugins/platforms/xcb/qxcbwindow.cpp Change-Id: Iad9ef6bf7d6111efba8232a7d9b46bb9974912f5
d7a5dc0c
/****************************************************************************
**
** 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();