Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbusintegrator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#include <qcoreapplication.h>
9#include <qelapsedtimer.h>
10#include <private/qlatch_p.h>
11#include <qloggingcategory.h>
12#include <qmetaobject.h>
13#include <qobject.h>
14#include <qsocketnotifier.h>
15#include <qstringlist.h>
16#include <qthread.h>
17#include <private/qlocking_p.h>
18#include <QtCore/qset.h>
19
20#include "qdbusargument.h"
24#include "qdbusmessage.h"
25#include "qdbusmetatype.h"
29#include "qdbusserver.h"
30#include "qdbusutil_p.h"
32#include "qdbusmessage_p.h"
33#include "qdbuscontext_p.h"
35
37
38#include <algorithm>
39#ifdef interface
40#undef interface
41#endif
42
43#ifndef QT_NO_DBUS
44
45QT_BEGIN_NAMESPACE
46
47using namespace Qt::StringLiterals;
48
49QT_IMPL_METATYPE_EXTERN(QDBusSlotCache)
50
51// used with dbus_server_allocate_data_slot
53
54Q_STATIC_LOGGING_CATEGORY(dbusIntegration, "qt.dbus.integration", QtWarningMsg)
55
56Q_CONSTINIT static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1);
57#define qDBusDebug if (::isDebugging.loadRelaxed() == 0); else qDebug
58
59static inline QDebug operator<<(QDebug dbg, const QThread *th)
60{
61 QDebugStateSaver saver(dbg);
62 dbg.nospace() << "QThread(ptr=" << (const void*)th;
63 if (th && !th->objectName().isEmpty())
64 dbg.nospace() << ", name=" << th->objectName();
65 else if (th)
66 dbg.nospace() << ", name=" << th->metaObject()->className();
67 dbg.nospace() << ')';
68 return dbg;
69}
70
71#if QDBUS_THREAD_DEBUG
72static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn)
73{
74 QDebugStateSaver saver(dbg);
75 dbg.nospace() << "QDBusConnection("
76 << "ptr=" << (const void*)conn
77 << ", name=" << conn->name
78 << ", baseService=" << conn->baseService
79 << ')';
80 return dbg;
81}
82
83void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn)
84{
85 qDBusDebug() << QThread::currentThread()
86 << "Qt D-Bus threading action" << action
87 << (condition == QDBusLockerBase::BeforeLock ? "before lock" :
88 condition == QDBusLockerBase::AfterLock ? "after lock" :
89 condition == QDBusLockerBase::BeforeUnlock ? "before unlock" :
90 condition == QDBusLockerBase::AfterUnlock ? "after unlock" :
91 condition == QDBusLockerBase::BeforePost ? "before event posting" :
92 condition == QDBusLockerBase::AfterPost ? "after event posting" :
93 condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" :
94 condition == QDBusLockerBase::AfterDeliver ? "after event delivery" :
95 condition == QDBusLockerBase::BeforeAcquire ? "before acquire" :
96 condition == QDBusLockerBase::AfterAcquire ? "after acquire" :
97 condition == QDBusLockerBase::BeforeRelease ? "before release" :
98 condition == QDBusLockerBase::AfterRelease ? "after release" :
99 "condition unknown")
100 << "in connection" << conn;
101}
102qdbusThreadDebugFunc qdbusThreadDebug = nullptr;
103#endif
104
106{
107public:
109 {
110 const auto locker = qt_scoped_lock(lock);
111 list.append(hook);
112 }
113
114 void invoke(const QDBusMessage &msg)
115 {
116 // Create a copy of the hook list here, so that the hooks can be called
117 // without holding the lock.
118 QList<QDBusSpyCallEvent::Hook> hookListCopy;
119 {
120 const auto locker = qt_scoped_lock(lock);
121 hookListCopy = list;
122 }
123
124 for (auto hook : std::as_const(hookListCopy))
125 hook(msg);
126 }
127
128private:
129 QBasicMutex lock;
131};
132
133Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
134
135extern "C" {
136
137 // libdbus-1 callbacks
138
139static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
140{
141 Q_ASSERT(timeout);
142 Q_ASSERT(data);
143
144 // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout));
145
146 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
147 Q_ASSERT(QThread::currentThread() == d->thread());
148
149 // we may get called from qDBusToggleTimeout
150 if (Q_UNLIKELY(!q_dbus_timeout_get_enabled(timeout)))
151 return false;
152
153 Q_ASSERT(d->timeouts.key(timeout, 0) == 0);
154
155 using namespace std::chrono_literals;
156 int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout) * 1ms); // no overflow possible
157 if (!timerId)
158 return false;
159
160 d->timeouts[timerId] = timeout;
161 return true;
162}
163
164static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
165{
166 Q_ASSERT(timeout);
167 Q_ASSERT(data);
168
169 // qDebug("removeTimeout");
170
171 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
172 Q_ASSERT(QThread::currentThread() == d->thread());
173
174 QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
175 while (it != d->timeouts.end()) {
176 if (it.value() == timeout) {
177 d->killTimer(it.key());
178 it = d->timeouts.erase(it);
179 break;
180 } else {
181 ++it;
182 }
183 }
184}
185
186static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
187{
188 Q_ASSERT(timeout);
189 Q_ASSERT(data);
190
191 //qDebug("ToggleTimeout");
192
193 qDBusRemoveTimeout(timeout, data);
194 qDBusAddTimeout(timeout, data);
195}
196
197static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
198{
199 Q_ASSERT(watch);
200 Q_ASSERT(data);
201
202 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
203 Q_ASSERT(QThread::currentThread() == d->thread());
204
205 int flags = q_dbus_watch_get_flags(watch);
206 int fd = q_dbus_watch_get_unix_fd(watch);
207
208 QDBusConnectionPrivate::Watcher watcher;
209
210 if (flags & DBUS_WATCH_READABLE) {
211 //qDebug("addReadWatch %d", fd);
212 watcher.watch = watch;
213 watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
214 watcher.read->setEnabled(q_dbus_watch_get_enabled(watch));
215 d->connect(watcher.read, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketRead);
216 }
217 if (flags & DBUS_WATCH_WRITABLE) {
218 //qDebug("addWriteWatch %d", fd);
219 watcher.watch = watch;
220 watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
221 watcher.write->setEnabled(q_dbus_watch_get_enabled(watch));
222 d->connect(watcher.write, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketWrite);
223 }
224 d->watchers.insert(fd, watcher);
225
226 return true;
227}
228
229static void qDBusRemoveWatch(DBusWatch *watch, void *data)
230{
231 Q_ASSERT(watch);
232 Q_ASSERT(data);
233
234 //qDebug("remove watch");
235
236 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
237 Q_ASSERT(QThread::currentThread() == d->thread());
238 int fd = q_dbus_watch_get_unix_fd(watch);
239
240 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
241 while (i != d->watchers.end() && i.key() == fd) {
242 if (i.value().watch == watch) {
243 delete i.value().read;
244 delete i.value().write;
245 i = d->watchers.erase(i);
246 } else {
247 ++i;
248 }
249 }
250}
251
252static void qDBusToggleWatch(DBusWatch *watch, void *data)
253{
254 Q_ASSERT(watch);
255 Q_ASSERT(data);
256
257 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
258 Q_ASSERT(QThread::currentThread() == d->thread());
259 int fd = q_dbus_watch_get_unix_fd(watch);
260
261 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
262 while (i != d->watchers.end() && i.key() == fd) {
263 if (i.value().watch == watch) {
264 bool enabled = q_dbus_watch_get_enabled(watch);
265 int flags = q_dbus_watch_get_flags(watch);
266
267 //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);
268
269 if (flags & DBUS_WATCH_READABLE && i.value().read)
270 i.value().read->setEnabled(enabled);
271 if (flags & DBUS_WATCH_WRITABLE && i.value().write)
272 i.value().write->setEnabled(enabled);
273 return;
274 }
275 ++i;
276 }
277}
278
279static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data)
280{
281 Q_ASSERT(connection);
282 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
283 if (new_status == DBUS_DISPATCH_DATA_REMAINS)
284 emit d->dispatchStatusChanged();
285}
286
287static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data)
288{
289 // ### We may want to separate the server from the QDBusConnectionPrivate
290 Q_ASSERT(server);
291 Q_ASSERT(connection);
292 Q_ASSERT(data);
293
295 if (!manager)
296 return;
297
298 // keep the connection alive
299 q_dbus_connection_ref(connection);
300 QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data);
301
302 // allow anonymous authentication
303 if (serverConnection->anonymousAuthenticationAllowed)
304 q_dbus_connection_set_allow_anonymous(connection, true);
305
306 QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate;
307
308 manager->addConnection(
309 "QDBusServer-"_L1 + QString::number(reinterpret_cast<qulonglong>(newConnection), 16),
310 newConnection);
311 {
312 QWriteLocker locker(&serverConnection->lock);
313 serverConnection->serverConnectionNames << newConnection->name;
314 }
315
316 // setPeer does the error handling for us
317 QDBusErrorInternal error;
318 newConnection->setPeer(connection, error);
319 newConnection->setDispatchEnabled(false);
320
321 QReadLocker serverLock(&serverConnection->lock);
322 if (!serverConnection->serverObject)
323 return;
324
325 // this is a queued connection and will resume in the QDBusServer's thread
326 QMetaObject::invokeMethod(serverConnection->serverObject, &QDBusServer::newConnection,
327 Qt::QueuedConnection, QDBusConnectionPrivate::q(newConnection));
328
329 // we've disabled dispatching of events, so now we post an event to the
330 // QDBusServer's thread in order to enable it after the
331 // QDBusServer::newConnection() signal has been received by the
332 // application's code
333
334 newConnection->enableDispatchDelayed(serverConnection->serverObject);
335}
336
337} // extern "C"
338
339static QByteArray buildMatchRule(const QString &service,
340 const QString &objectPath, const QString &interface,
341 const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/)
342{
343 QString result;
344 result += "type='signal',"_L1;
345 const auto keyValue = "%1='%2',"_L1;
346
347 if (!service.isEmpty())
348 result += keyValue.arg("sender"_L1, service);
349 if (!objectPath.isEmpty())
350 result += keyValue.arg("path"_L1, objectPath);
351 if (!interface.isEmpty())
352 result += keyValue.arg("interface"_L1, interface);
353 if (!member.isEmpty())
354 result += keyValue.arg("member"_L1, member);
355
356 // add the argument string-matching now
357 if (!argMatch.args.isEmpty()) {
358 const QString keyValue = "arg%1='%2',"_L1;
359 for (int i = 0; i < argMatch.args.size(); ++i)
360 if (!argMatch.args.at(i).isNull())
361 result += keyValue.arg(i).arg(argMatch.args.at(i));
362 }
363 if (!argMatch.arg0namespace.isEmpty()) {
364 result += "arg0namespace='%1',"_L1.arg(argMatch.arg0namespace);
365 }
366
367 result.chop(1); // remove ending comma
368 return result.toLatin1();
369}
370
371static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
372 const QString &fullpath, int &usedLength,
373 QDBusConnectionPrivate::ObjectTreeNode &result)
374{
375 if (!fullpath.compare("/"_L1) && root->obj) {
376 usedLength = 1;
377 result = *root;
378 return root;
379 }
380 int start = 0;
381 int length = fullpath.size();
382 if (fullpath.at(0) == u'/')
383 start = 1;
384
385 // walk the object tree
386 const QDBusConnectionPrivate::ObjectTreeNode *node = root;
387 while (start < length && node) {
388 if (node->flags & QDBusConnection::ExportChildObjects)
389 break;
390 if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath))
391 break;
392 int end = fullpath.indexOf(u'/', start);
393 end = (end == -1 ? length : end);
394 QStringView pathComponent = QStringView{fullpath}.mid(start, end - start);
395
396 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
397 std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent);
398 if (it != node->children.constEnd() && it->name == pathComponent)
399 // match
400 node = &(*it);
401 else
402 node = nullptr;
403
404 start = end + 1;
405 }
406
407 // found our object
408 usedLength = (start > length ? length : start);
409 if (node) {
410 if (node->obj || !node->children.isEmpty())
411 result = *node;
412 else
413 // there really is no object here
414 // we're just looking at an unused space in the QList
415 node = nullptr;
416 }
417 return node;
418}
419
420static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
421 const QString &fullpath, int start)
422{
423 int length = fullpath.size();
424
425 // any object in the tree can tell us to switch to its own object tree:
426 const QDBusConnectionPrivate::ObjectTreeNode *node = root;
427 if (node && node->flags & QDBusConnection::ExportChildObjects) {
428 QObject *obj = node->obj;
429
430 while (obj) {
431 if (start >= length)
432 // we're at the correct level
433 return obj;
434
435 int pos = fullpath.indexOf(u'/', start);
436 pos = (pos == -1 ? length : pos);
437 auto pathComponent = QStringView{fullpath}.mid(start, pos - start);
438
439 // find a child with the proper name
440 QObject *next = nullptr;
441 for (QObject *child : std::as_const(obj->children())) {
442 if (child->objectName() == pathComponent) {
443 next = child;
444 break;
445 }
446 }
447
448 if (!next)
449 break;
450
451 obj = next;
452 start = pos + 1;
453 }
454 }
455
456 // object not found
457 return nullptr;
458}
459
460static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode)
461{
462 QDBusConnectionPrivate::ArgMatchRules matchArgs;
463 if (service.endsWith(u'*')) {
464 matchArgs.arg0namespace = service.chopped(1);
465 matchArgs.args << QString();
466 }
467 else
468 matchArgs.args << service;
469
470 switch (mode) {
471 case QDBusServiceWatcher::WatchForOwnerChange:
472 break;
473
474 case QDBusServiceWatcher::WatchForRegistration:
475 matchArgs.args << QString::fromLatin1("", 0);
476 break;
477
478 case QDBusServiceWatcher::WatchForUnregistration:
479 matchArgs.args << QString() << QString::fromLatin1("", 0);
480 break;
481 }
482 return matchArgs;
483}
484
485
486extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook);
488{
489 auto *hooks = qDBusSpyHookList();
490 if (!hooks)
491 return;
492
493 hooks->add(hook);
494}
495
497{
498 // Reinsert the message into the processing queue for the connection.
499 // This is done in the destructor so the message is reinserted even if
500 // QCoreApplication is destroyed.
501 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(const_cast<QObject *>(sender()));
502 qDBusDebug() << d << "message spies done for" << msg;
503 emit d->spyHooksFinished(msg);
504}
505
507{
508 invokeSpyHooks(msg);
509}
510
511inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg)
512{
513 if (!qDBusSpyHookList.exists())
514 return;
515
516 qDBusSpyHookList->invoke(msg);
517}
518
519extern "C" {
520static DBusHandlerResult
521qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
522{
523 Q_ASSERT(data);
524 Q_UNUSED(connection);
525 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
526 if (d->mode == QDBusConnectionPrivate::InvalidMode)
528
529 QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->connectionCapabilities());
530 qDBusDebug() << d << "got message (signal):" << amsg;
531
532 return d->handleMessage(amsg) ?
535}
536}
537
538bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
539{
540 if (!ref.loadRelaxed())
541 return false;
542
543 // local message are always delivered, regardless of filtering
544 // or whether the dispatcher is enabled
545 bool isLocal = QDBusMessagePrivate::isLocal(amsg);
546
547 if (!dispatchEnabled && !isLocal) {
548 // queue messages only, we'll handle them later
549 qDBusDebug() << this << "delivery is suspended";
550 pendingMessages << amsg;
551 return amsg.type() == QDBusMessage::MethodCallMessage;
552 }
553
554 switch (amsg.type()) {
555 case QDBusMessage::SignalMessage:
556 handleSignal(amsg);
557 // if there are any other filters in this DBusConnection,
558 // let them see the signal too
559 return false;
560 case QDBusMessage::MethodCallMessage:
561 // run it through the spy filters (if any) before the regular processing:
562 // a) if it's a local message, we're in the caller's thread, so invoke the filter directly
563 // b) if it's an external message, post to the main thread
564 if (Q_UNLIKELY(qDBusSpyHookList.exists()) && QCoreApplication::instanceExists()) {
565 if (isLocal) {
566 Q_ASSERT(QThread::currentThread() != thread());
567 qDBusDebug() << this << "invoking message spies directly";
568 QDBusSpyCallEvent::invokeSpyHooks(amsg);
569 } else {
570 qDBusDebug() << this << "invoking message spies via event";
571 QCoreApplication::postEvent(
572 qApp, new QDBusSpyCallEvent(this, QDBusConnection(this), amsg));
573
574 // we'll be called back, so return
575 return true;
576 }
577 }
578
579 handleObjectCall(amsg);
580 return true;
581 case QDBusMessage::ReplyMessage:
582 case QDBusMessage::ErrorMessage:
583 case QDBusMessage::InvalidMessage:
584 return false; // we don't handle those here
585 }
586
587 return false;
588}
589
590static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
591{
592 for (QDBusConnectionPrivate::ObjectTreeNode &node : haystack.children)
593 huntAndDestroy(needle, node);
594
595 auto isInactive = [](const QDBusConnectionPrivate::ObjectTreeNode &node) { return !node.isActive(); };
596 haystack.children.removeIf(isInactive);
597
598 if (needle == haystack.obj) {
599 haystack.obj = nullptr;
600 haystack.flags = {};
601 }
602}
603
604static void huntAndUnregister(const QList<QStringView> &pathComponents, int i,
605 QDBusConnection::UnregisterMode mode,
606 QDBusConnectionPrivate::ObjectTreeNode *node)
607{
608 if (pathComponents.size() == i) {
609 // found it
610 node->obj = nullptr;
611 node->flags = {};
612
613 if (mode == QDBusConnection::UnregisterTree) {
614 // clear the sub-tree as well
615 node->children.clear(); // can't disconnect the objects because we really don't know if they can
616 // be found somewhere else in the path too
617 }
618 } else {
619 // keep going
620 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
621 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
622 std::lower_bound(node->children.begin(), end, pathComponents.at(i));
623 if (it == end || it->name != pathComponents.at(i))
624 return; // node not found
625
626 huntAndUnregister(pathComponents, i + 1, mode, &(*it));
627 if (!it->isActive())
628 node->children.erase(it);
629 }
630}
631
632static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
633 QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack,
634 bool isScriptable, bool isAdaptor, const QString &path = QString())
635{
636 for (const QDBusConnectionPrivate::ObjectTreeNode &node : std::as_const(haystack.children)) {
637 if (node.isActive()) {
638 huntAndEmit(connection, msg, needle, node, isScriptable, isAdaptor,
639 path + u'/' + node.name);
640 }
641 }
642
643 if (needle == haystack.obj) {
644 // is this a signal we should relay?
645 if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0)
646 return; // no: it comes from an adaptor and we're not exporting adaptors
647 else if (!isAdaptor) {
648 int mask = isScriptable
649 ? QDBusConnection::ExportScriptableSignals
650 : QDBusConnection::ExportNonScriptableSignals;
651 if ((haystack.flags & mask) == 0)
652 return; // signal was not exported
653 }
654
655 QByteArray p = path.toLatin1();
656 if (p.isEmpty())
657 p = "/";
658 qDBusDebug() << QThread::currentThread() << "emitting signal at" << p;
659 DBusMessage *msg2 = q_dbus_message_copy(msg);
660 q_dbus_message_set_path(msg2, p);
661 q_dbus_connection_send(connection, msg2, nullptr);
663 }
664}
665
666static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
667 const QString &signature_, QList<QMetaType> &metaTypes)
668{
669 QByteArray msgSignature = signature_.toLatin1();
670 QString parametersErrorMsg;
671
672 for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
673 QMetaMethod mm = mo->method(idx);
674
675 // check access:
676 if (mm.access() != QMetaMethod::Public)
677 continue;
678
679 // check type:
680 if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method)
681 continue;
682
683 // check name:
684 if (mm.name() != name)
685 continue;
686
687 QMetaType returnType = mm.returnMetaType();
688 bool isAsync = qDBusCheckAsyncTag(mm.tag());
689 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
690
691 // consistency check:
692 if (isAsync && returnType.id() != QMetaType::Void)
693 continue;
694
695 QString errorMsg;
696 int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg);
697 if (inputCount == -1) {
698 parametersErrorMsg = errorMsg;
699 continue; // problem parsing
700 }
701
702 metaTypes[0] = returnType;
703 bool hasMessage = false;
704 if (inputCount > 0 &&
705 metaTypes.at(inputCount) == QDBusMetaTypeId::message()) {
706 // "no input parameters" is allowed as long as the message meta type is there
707 hasMessage = true;
708 --inputCount;
709 }
710
711 // try to match the parameters
712 int i;
713 QByteArray reconstructedSignature;
714 for (i = 1; i <= inputCount; ++i) {
715 const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) );
716 if (!typeSignature)
717 break; // invalid
718
719 reconstructedSignature += typeSignature;
720 if (!msgSignature.startsWith(reconstructedSignature))
721 break;
722 }
723
724 if (reconstructedSignature != msgSignature)
725 continue; // we didn't match them all
726
727 if (hasMessage)
728 ++i;
729
730 // make sure that the output parameters have signatures too
731 if (returnType.isValid() && returnType.id() != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == nullptr)
732 continue;
733
734 bool ok = true;
735 for (int j = i; ok && j < metaTypes.size(); ++j)
736 if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == nullptr)
737 ok = false;
738 if (!ok)
739 continue;
740
741 // consistency check:
742 if (isAsync && metaTypes.size() > i + 1)
743 continue;
744
745 if (mm.methodType() == QMetaMethod::Slot) {
746 if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
747 continue; // scriptable slots not exported
748 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
749 continue; // non-scriptable slots not exported
750 } else {
751 if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0)
752 continue; // scriptable invokables not exported
753 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0)
754 continue; // non-scriptable invokables not exported
755 }
756
757 // if we got here, this slot matched
758 return idx;
759 }
760
761 // no slot matched
762 if (!parametersErrorMsg.isEmpty()) {
763 qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s: %ls",
764 name.constData(), qUtf16Printable(parametersErrorMsg));
765 } else {
766 qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s, no slot matched",
767 name.constData());
768 }
769 return -1;
770}
771
772/*!
773 \internal
774 Enables or disables the delivery of incoming method calls and signals. If
775 \a enable is true, this will also cause any queued, pending messages to be
776 delivered.
777 */
778void QDBusConnectionPrivate::setDispatchEnabled(bool enable)
779{
780 checkThread();
781 dispatchEnabled = enable;
782 if (enable)
783 emit dispatchStatusChanged();
784}
785
786static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1;
787
788QDBusCallDeliveryEvent *QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
789 QObject *object, int idx,
790 const QList<QMetaType> &metaTypes,
791 const QDBusMessage &msg)
792{
793 Q_ASSERT(object);
794
795 int n = metaTypes.size() - 1;
796 if (metaTypes[n] == QDBusMetaTypeId::message())
797 --n;
798
799 if (msg.arguments().size() < n)
800 return nullptr; // too few arguments
801
802 // check that types match
803 for (int i = 0; i < n; ++i)
804 if (metaTypes.at(i + 1) != msg.arguments().at(i).metaType() &&
805 msg.arguments().at(i).metaType() != QMetaType::fromType<QDBusArgument>())
806 return nullptr; // no match
807
808 // we can deliver
809 // prepare for the call
810 if (target == object)
811 return DIRECT_DELIVERY;
812 return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes);
813}
814
815void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
816 const QDBusMessage &msg)
817{
818 // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
819 // that was received from D-Bus
820 //
821 // Signals are delivered to slots if the parameters match
822 // Slots can have less parameters than there are on the message
823 // Slots can optionally have one final parameter that is a QDBusMessage
824 // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
825 QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg);
826 if (call == DIRECT_DELIVERY) {
827 // short-circuit delivery
828 Q_ASSERT(this == hook.obj);
829 deliverCall(this, msg, hook.params, hook.midx);
830 return;
831 }
832 if (call)
833 postEventToThread(ActivateSignalAction, hook.obj, call);
834}
835
836bool QDBusConnectionPrivate::activateCall(QObject *object, QDBusConnection::RegisterOptions flags,
837 const QDBusMessage &msg)
838{
839 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
840 // to a slot on the object.
841 //
842 // The call is delivered to the first slot that matches the following conditions:
843 // - has the same name as the message's target member
844 // - ALL of the message's types are found in slot's parameter list
845 // - optionally has one more parameter of type QDBusMessage
846 // If none match, then the slot of the same name as the message target and with
847 // the first type of QDBusMessage is delivered.
848 //
849 // The D-Bus specification requires that all MethodCall messages be replied to, unless the
850 // caller specifically waived this requirement. This means that we inspect if the user slot
851 // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
852 // QDBusMessage parameter, it cannot generate a reply.
853 //
854 // When a return message is generated, the slot's return type, if any, will be placed
855 // in the message's first position. If there are non-const reference parameters to the
856 // slot, they must appear at the end and will be placed in the subsequent message
857 // positions.
858
859 static const char cachePropertyName[] = "_qdbus_slotCache";
860
861 if (!object)
862 return false;
863
864 Q_ASSERT_X(QThread::currentThread() == object->thread(),
865 "QDBusConnection: internal threading error",
866 "function called for an object that is in another thread!!");
867
868 QDBusSlotCache slotCache =
869 qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName));
870 QString cacheKey = msg.member(), signature = msg.signature();
871 if (!signature.isEmpty()) {
872 cacheKey.reserve(cacheKey.size() + 1 + signature.size());
873 cacheKey += u'.';
874 cacheKey += signature;
875 }
876
877 QDBusSlotCache::Key compoundKey{ std::move(cacheKey), flags };
878 QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(compoundKey);
879 if (cacheIt == slotCache.hash.constEnd()) {
880 // not cached, analyze the meta object
881 const QMetaObject *mo = object->metaObject();
882 QByteArray memberName = msg.member().toUtf8();
883
884 // find a slot that matches according to the rules above
885 QDBusSlotCache::Data slotData;
886 slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes);
887 if (slotData.slotIdx == -1) {
888 // ### this is where we want to add the connection as an arg too
889 // try with no parameters, but with a QDBusMessage
890 slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes);
891 if (slotData.metaTypes.size() != 2 ||
892 slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) {
893 // not found
894 // save the negative lookup
895 slotData.slotIdx = -1;
896 slotData.metaTypes.clear();
897 slotCache.hash.insert(compoundKey, slotData);
898 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
899
900 qCWarning(dbusIntegration).nospace() << "Could not find slot " << mo->className()
901 << "::" << memberName.constData();
902 return false;
903 }
904 }
905
906 // save to the cache
907 slotCache.hash.insert(compoundKey, slotData);
908 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
909
910 // found the slot to be called
911 deliverCall(object, msg, slotData.metaTypes, slotData.slotIdx);
912 return true;
913 } else if (cacheIt->slotIdx == -1) {
914 // negative cache
915 return false;
916 } else {
917 // use the cache
918 deliverCall(object, msg, cacheIt->metaTypes, cacheIt->slotIdx);
919 return true;
920 }
921 return false;
922}
923
924void QDBusConnectionPrivate::deliverCall(QObject *object, const QDBusMessage &msg,
925 const QList<QMetaType> &metaTypes, int slotIdx)
926{
927 Q_ASSERT_X(!object || QThread::currentThread() == object->thread(),
928 "QDBusConnection: internal threading error",
929 "function called for an object that is in another thread!!");
930
931 QVarLengthArray<void *, 10> params;
932 params.reserve(metaTypes.size());
933
934 QVarLengthArray<QVariant, 10> auxParameters; // we cannot allow reallocation here, since we
935 auxParameters.reserve(metaTypes.size()); // keep references to the entries
936
937 // let's create the parameter list
938
939 // first one is the return type -- add it below
940 params.append(nullptr);
941
942 // add the input parameters
943 int i;
944 int pCount = qMin(msg.arguments().size(), metaTypes.size() - 1);
945 for (i = 1; i <= pCount; ++i) {
946 auto id = metaTypes[i];
947 if (id == QDBusMetaTypeId::message())
948 break;
949
950 const QList<QVariant> args = msg.arguments();
951 const QVariant &arg = args.at(i - 1);
952 if (arg.metaType() == id)
953 // no conversion needed
954 params.append(const_cast<void *>(arg.constData()));
955 else if (arg.metaType() == QMetaType::fromType<QDBusArgument>()) {
956 // convert to what the function expects
957 auxParameters.append(QVariant(QMetaType(id)));
958
959 const QDBusArgument &in =
960 *reinterpret_cast<const QDBusArgument *>(arg.constData());
961 QVariant &out = auxParameters[auxParameters.size() - 1];
962
963 if (Q_UNLIKELY(!QDBusMetaType::demarshall(in, out.metaType(), out.data())))
964 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!",
965 out.typeName(), out.metaType().id());
966
967 params.append(const_cast<void *>(out.constData()));
968 } else {
969 qFatal("Internal error: got invalid meta type %d (%s) "
970 "when trying to convert to meta type %d (%s)",
971 arg.metaType().id(), arg.metaType().name(),
972 id.id(), id.name());
973 }
974 }
975
976 if (metaTypes.size() > i && metaTypes[i] == QDBusMetaTypeId::message()) {
977 params.append(const_cast<void*>(static_cast<const void*>(&msg)));
978 ++i;
979 }
980
981 // output arguments
982 const int numMetaTypes = metaTypes.size();
983 QVariantList outputArgs;
984 if (metaTypes[0].id() != QMetaType::Void && metaTypes[0].isValid()) {
985 outputArgs.reserve(numMetaTypes - i + 1);
986 QVariant arg{QMetaType(metaTypes[0])};
987 outputArgs.append( arg );
988 params[0] = const_cast<void*>(outputArgs.at( outputArgs.size() - 1 ).constData());
989 } else {
990 outputArgs.reserve(numMetaTypes - i);
991 }
992
993 for ( ; i < numMetaTypes; ++i) {
994 QVariant arg{QMetaType(metaTypes[i])};
995 outputArgs.append( arg );
996 params.append(const_cast<void*>(outputArgs.at( outputArgs.size() - 1 ).constData()));
997 }
998
999 // make call:
1000 bool fail;
1001 if (!object) {
1002 fail = true;
1003 } else {
1004 // FIXME: save the old sender!
1005 QDBusContextPrivate context(QDBusConnection(this), msg);
1006 QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context);
1007
1008 QPointer<QObject> ptr = object;
1009 fail = object->qt_metacall(QMetaObject::InvokeMetaMethod,
1010 slotIdx, params.data()) >= 0;
1011 // the object might be deleted in the slot
1012 if (!ptr.isNull())
1013 QDBusContextPrivate::set(object, old);
1014 }
1015
1016 // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
1017 // yet.
1018 if (msg.isReplyRequired() && !msg.isDelayedReply()) {
1019 if (!fail) {
1020 // normal reply
1021 qDBusDebug() << this << "Automatically sending reply:" << outputArgs;
1022 send(msg.createReply(outputArgs));
1023 } else {
1024 // generate internal error
1025 qCWarning(dbusIntegration, "Internal error: Failed to deliver message");
1026 send(msg.createErrorReply(QDBusError::InternalError, "Failed to deliver message"_L1));
1027 }
1028 }
1029
1030 return;
1031}
1032
1033QDBusConnectionPrivate::QDBusConnectionPrivate()
1034 : ref(1),
1035 mode(InvalidMode),
1036 busService(nullptr),
1037 connection(nullptr),
1038 rootNode(QStringLiteral("/")),
1039 anonymousAuthenticationAllowed(false),
1040 dispatchEnabled(true),
1041 isAuthenticated(false)
1042{
1043 static const bool threads = q_dbus_threads_init_default();
1044 Q_UNUSED(threads);
1045 if (::isDebugging.loadRelaxed() == -1)
1046 ::isDebugging.storeRelaxed(qEnvironmentVariableIntValue("QDBUS_DEBUG"));
1047
1048#ifdef QDBUS_THREAD_DEBUG
1049 if (::isDebugging.loadRelaxed() > 1)
1050 qdbusThreadDebug = qdbusDefaultThreadDebug;
1051#endif
1052
1053 QDBusMetaTypeId::init();
1054 connect(this, &QDBusConnectionPrivate::dispatchStatusChanged,
1055 this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection);
1056 connect(this, &QDBusConnectionPrivate::spyHooksFinished,
1057 this, &QDBusConnectionPrivate::handleObjectCall, Qt::QueuedConnection);
1058 connect(this, &QDBusConnectionPrivate::messageNeedsSending,
1059 this, &QDBusConnectionPrivate::sendInternal);
1060
1061 rootNode.flags = {};
1062
1063 // prepopulate watchedServices:
1064 // we know that the owner of org.freedesktop.DBus is itself
1065 watchedServices.insert(QDBusUtil::dbusService(), WatchedServiceData(QDBusUtil::dbusService(), 1));
1066
1067 // prepopulate matchRefCounts:
1068 // we know that org.freedesktop.DBus will never change owners
1069 matchRefCounts.insert("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", 1);
1070}
1071
1072QDBusConnectionPrivate::~QDBusConnectionPrivate()
1073{
1074 if (thread() && thread() != QThread::currentThread())
1075 qCWarning(dbusIntegration,
1076 "QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! "
1077 "Timer and socket errors will follow and the program will probably crash",
1078 qPrintable(name));
1079
1080 auto lastMode = mode; // reset on connection close
1081 closeConnection();
1082 qDeleteAll(cachedMetaObjects);
1083
1084 if (lastMode == ClientMode || lastMode == PeerMode) {
1085 // the bus service object holds a reference back to us;
1086 // we need to destroy it before we finish destroying ourselves
1087 Q_ASSERT(ref.loadRelaxed() == 0);
1088 QObject *obj = (QObject *)busService;
1089 if (obj) {
1090 disconnect(obj, nullptr, this, nullptr);
1091 delete obj;
1092 }
1093 if (connection)
1094 q_dbus_connection_unref(connection);
1095 connection = nullptr;
1096 } else if (lastMode == ServerMode) {
1097 if (server)
1098 q_dbus_server_unref(server);
1099 server = nullptr;
1100 }
1101}
1102
1103void QDBusConnectionPrivate::collectAllObjects(QDBusConnectionPrivate::ObjectTreeNode &haystack,
1104 QSet<QObject *> &set)
1105{
1106 for (ObjectTreeNode &child : haystack.children)
1107 collectAllObjects(child, set);
1108
1109 if (haystack.obj)
1110 set.insert(haystack.obj);
1111}
1112
1113void QDBusConnectionPrivate::closeConnection()
1114{
1115 QDBusWriteLocker locker(CloseConnectionAction, this);
1116 qDBusDebug() << this << "Disconnected";
1117 ConnectionMode oldMode = mode;
1118 mode = InvalidMode; // prevent reentrancy
1119 baseService.clear();
1120
1121 if (oldMode == ServerMode && server) {
1122 q_dbus_server_disconnect(server);
1123 q_dbus_server_free_data_slot(&server_slot);
1124 }
1125
1126 if (oldMode == ClientMode || oldMode == PeerMode) {
1127 if (connection) {
1128 q_dbus_connection_close(connection);
1129 // send the "close" message
1130 while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
1131 ;
1132 }
1133 }
1134
1135 for (QDBusPendingCallPrivate *call : std::as_const(pendingCalls)) {
1136 if (!call->ref.deref())
1137 delete call;
1138 }
1139 pendingCalls.clear();
1140
1141 // Disconnect all signals from signal hooks and from the object tree to
1142 // avoid QObject::destroyed being sent to dbus daemon thread which has
1143 // already quit. We need to make sure we disconnect exactly once per
1144 // object, because if we tried a second time, we might be hitting a
1145 // dangling pointer.
1146 QSet<QObject *> allObjects;
1147 collectAllObjects(rootNode, allObjects);
1148 for (const SignalHook &signalHook : std::as_const(signalHooks))
1149 allObjects.insert(signalHook.obj);
1150
1151 // now disconnect ourselves
1152 for (QObject *obj : std::as_const(allObjects))
1153 obj->disconnect(this);
1154}
1155
1156void QDBusConnectionPrivate::handleDBusDisconnection()
1157{
1158 while (!pendingCalls.isEmpty())
1159 processFinishedCall(pendingCalls.first());
1160}
1161
1162void QDBusConnectionPrivate::checkThread()
1163{
1164 Q_ASSERT(thread() == QDBusConnectionManager::instance());
1165 Q_ASSERT(QThread::currentThread() == thread());
1166}
1167
1168bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error)
1169{
1170 if (!error)
1171 return false; // no error
1172
1173 //lock.lockForWrite();
1174 lastError = error;
1175 //lock.unlock();
1176 return true;
1177}
1178
1179void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
1180{
1181 {
1182 DBusTimeout *timeout = timeouts.value(e->timerId(), nullptr);
1183 if (timeout)
1184 q_dbus_timeout_handle(timeout);
1185 }
1186
1187 doDispatch();
1188}
1189
1190void QDBusConnectionPrivate::doDispatch()
1191{
1192 if (mode == ClientMode || mode == PeerMode) {
1193 if (dispatchEnabled && !pendingMessages.isEmpty()) {
1194 // dispatch previously queued messages
1195 for (QDBusMessage &message : pendingMessages) {
1196 qDBusDebug() << this << "dequeueing message" << message;
1197 handleMessage(std::move(message));
1198 }
1199 pendingMessages.clear();
1200 }
1201 while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ;
1202 }
1203}
1204
1205void QDBusConnectionPrivate::socketRead(qintptr fd)
1206{
1207 WatcherHash::ConstIterator it = watchers.constFind(fd);
1208 while (it != watchers.constEnd() && it.key() == fd) {
1209 if (it->watch && it->read && it->read->isEnabled()) {
1210 if (!q_dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
1211 qDebug("OUT OF MEM");
1212 break;
1213 }
1214 ++it;
1215 }
1216 if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1217 && q_dbus_connection_get_is_authenticated(connection))
1218 handleAuthentication();
1219 doDispatch();
1220}
1221
1222void QDBusConnectionPrivate::socketWrite(qintptr fd)
1223{
1224 WatcherHash::ConstIterator it = watchers.constFind(fd);
1225 while (it != watchers.constEnd() && it.key() == fd) {
1226 if (it->watch && it->write && it->write->isEnabled()) {
1227 if (!q_dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
1228 qDebug("OUT OF MEM");
1229 break;
1230 }
1231 ++it;
1232 }
1233 if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1234 && q_dbus_connection_get_is_authenticated(connection))
1235 handleAuthentication();
1236}
1237
1238void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
1239{
1240 QDBusWriteLocker locker(ObjectDestroyedAction, this);
1241 huntAndDestroy(obj, rootNode);
1242
1243 SignalHookHash::iterator sit = signalHooks.begin();
1244 while (sit != signalHooks.end()) {
1245 if (static_cast<QObject *>(sit.value().obj) == obj)
1246 sit = removeSignalHookNoLock(sit);
1247 else
1248 ++sit;
1249 }
1250
1251 obj->disconnect(this);
1252}
1253
1254void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId,
1255 const QVariantList &args)
1256{
1257 QString interface = qDBusInterfaceFromMetaObject(mo);
1258
1259 QMetaMethod mm = mo->method(signalId);
1260 QByteArray memberName = mm.name();
1261
1262 // check if it's scriptable
1263 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
1264 bool isAdaptor = false;
1265 for ( ; mo; mo = mo->superClass())
1266 if (mo == &QDBusAbstractAdaptor::staticMetaObject) {
1267 isAdaptor = true;
1268 break;
1269 }
1270
1271 checkThread();
1272 QDBusReadLocker locker(RelaySignalAction, this);
1273 QDBusMessage message = QDBusMessage::createSignal("/"_L1, interface,
1274 QLatin1StringView(memberName));
1275 QDBusMessagePrivate::setParametersValidated(message, true);
1276 message.setArguments(args);
1277 QDBusError error;
1278 DBusMessage *msg =
1279 QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
1280 if (!msg) {
1281 qCWarning(dbusIntegration, "QDBusConnection: Could not emit signal %s.%s: %s",
1282 qPrintable(interface), memberName.constData(), qPrintable(error.message()));
1283 lastError = error;
1284 return;
1285 }
1286
1287 //qDBusDebug() << "Emitting signal" << message;
1288 //qDBusDebug() << "for paths:";
1289 q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1290 huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor);
1291 q_dbus_message_unref(msg);
1292}
1293
1294void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name,
1295 const QString &oldOwner, const QString &newOwner)
1296{
1297// QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this);
1298 WatchedServicesHash::Iterator it = watchedServices.find(name);
1299 if (it == watchedServices.end())
1300 return;
1301 if (oldOwner != it->owner)
1302 qCWarning(dbusIntegration,
1303 "QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'",
1304 qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner));
1305
1306 qDBusDebug() << this << "Updating name" << name << "from" << oldOwner << "to" << newOwner;
1307 it->owner = newOwner;
1308}
1309
1310int QDBusConnectionPrivate::findSlot(QObject *obj, const QByteArray &normalizedName,
1311 QList<QMetaType> &params, QString &errorMsg)
1312{
1313 errorMsg.clear();
1314 int midx = obj->metaObject()->indexOfMethod(normalizedName);
1315 if (midx == -1)
1316 return -1;
1317
1318 int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params, errorMsg);
1319 if (inputCount == -1 || inputCount + 1 != params.size())
1320 return -1;
1321
1322 return midx;
1323}
1324
1325bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
1326 const QString &service, const QString &path,
1327 const QString &interface, const QString &name,
1328 const ArgMatchRules &argMatch, QObject *receiver,
1329 const char *signal, int minMIdx, bool buildSignature,
1330 QString &errorMsg)
1331{
1332 QByteArray normalizedName = signal + 1;
1333 hook.midx = findSlot(receiver, signal + 1, hook.params, errorMsg);
1334 if (hook.midx == -1) {
1335 normalizedName = QMetaObject::normalizedSignature(signal + 1);
1336 hook.midx = findSlot(receiver, normalizedName, hook.params, errorMsg);
1337 }
1338 if (hook.midx < minMIdx) {
1339 return false;
1340 }
1341
1342 hook.service = service;
1343 hook.path = path;
1344 hook.obj = receiver;
1345 hook.argumentMatch = argMatch;
1346
1347 // build the D-Bus signal name and signature
1348 // This should not happen for QDBusConnection::connect, use buildSignature here, since
1349 // QDBusConnection::connect passes false and everything else uses true
1350 QString mname = name;
1351 if (buildSignature && mname.isNull()) {
1352 normalizedName.truncate(normalizedName.indexOf('('));
1353 mname = QString::fromUtf8(normalizedName);
1354 }
1355 key = mname;
1356 key.reserve(interface.size() + 1 + mname.size());
1357 key += u':';
1358 key += interface;
1359
1360 if (buildSignature) {
1361 hook.signature.clear();
1362 for (int i = 1; i < hook.params.size(); ++i)
1363 if (hook.params.at(i) != QDBusMetaTypeId::message())
1364 hook.signature += QLatin1StringView(QDBusMetaType::typeToSignature(hook.params.at(i)));
1365 }
1366
1367 hook.matchRule = buildMatchRule(service, path, interface, mname, argMatch, hook.signature);
1368 return true; // connect to this signal
1369}
1370
1371void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code)
1372{
1373 if (code == QDBusError::UnknownMethod) {
1374 QString interfaceMsg;
1375 if (msg.interface().isEmpty())
1376 interfaceMsg = "any interface"_L1;
1377 else
1378 interfaceMsg = "interface '%1'"_L1.arg(msg.interface());
1379
1380 send(msg.createErrorReply(code, "No such method '%1' in %2 at object path '%3' "
1381 "(signature '%4')"_L1
1382 .arg(msg.member(), interfaceMsg, msg.path(), msg.signature())));
1383 } else if (code == QDBusError::UnknownInterface) {
1384 send(msg.createErrorReply(QDBusError::UnknownInterface,
1385 "No such interface '%1' at object path '%2'"_L1
1386 .arg(msg.interface(), msg.path())));
1387 } else if (code == QDBusError::UnknownObject) {
1388 send(msg.createErrorReply(QDBusError::UnknownObject,
1389 "No such object path '%1'"_L1.arg(msg.path())));
1390 }
1391}
1392
1393bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node,
1394 const QDBusMessage &msg)
1395{
1396 // object may be null
1397 const QString interface = msg.interface();
1398
1399 if (interface.isEmpty() || interface == QDBusUtil::dbusInterfaceIntrospectable()) {
1400 if (msg.member() == "Introspect"_L1 && msg.signature().isEmpty()) {
1401 //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg;
1402 QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node, msg.path()));
1403 send(reply);
1404 return true;
1405 }
1406
1407 if (!interface.isEmpty()) {
1408 sendError(msg, QDBusError::UnknownMethod);
1409 return true;
1410 }
1411 }
1412
1413 if (node.obj && (interface.isEmpty() ||
1414 interface == QDBusUtil::dbusInterfaceProperties())) {
1415 //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg;
1416
1417 QDBusContextPrivate context(QDBusConnection(this), msg);
1418 QDBusContextPrivate *old = QDBusContextPrivate::set(node.obj, &context);
1419 auto guard = qScopeGuard([&node, old]{
1420 QDBusContextPrivate::set(node.obj, old);
1421 });
1422
1423 if (msg.member() == "Get"_L1 && msg.signature() == "ss"_L1) {
1424 QDBusMessage reply = qDBusPropertyGet(node, msg);
1425 if (!msg.isDelayedReply()) {
1426 send(reply);
1427 }
1428 return true;
1429 } else if (msg.member() == "Set"_L1 && msg.signature() == "ssv"_L1) {
1430 QDBusMessage reply = qDBusPropertySet(node, msg);
1431 if (!msg.isDelayedReply()) {
1432 send(reply);
1433 }
1434 return true;
1435 } else if (msg.member() == "GetAll"_L1 && msg.signature() == "s"_L1) {
1436 QDBusMessage reply = qDBusPropertyGetAll(node, msg);
1437 if (!msg.isDelayedReply()) {
1438 send(reply);
1439 }
1440 return true;
1441 }
1442
1443 if (!interface.isEmpty()) {
1444 sendError(msg, QDBusError::UnknownMethod);
1445 return true;
1446 }
1447 }
1448
1449 return false;
1450}
1451
1452void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg,
1453 int pathStartPos)
1454{
1455 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
1456 // on the object.
1457 //
1458 // The call is routed through the adaptor sub-objects if we have any
1459
1460 // object may be null
1461
1462 if (node.flags & QDBusConnectionPrivate::VirtualObject) {
1463 if (node.treeNode->handleMessage(msg, q(this))) {
1464 return;
1465 } else {
1466 if (activateInternalFilters(node, msg))
1467 return;
1468 }
1469 }
1470
1471 if (pathStartPos != msg.path().size()) {
1472 node.flags &= ~QDBusConnection::ExportAllSignals;
1473 node.obj = findChildObject(&node, msg.path(), pathStartPos);
1474 if (!node.obj) {
1475 sendError(msg, QDBusError::UnknownObject);
1476 return;
1477 }
1478 }
1479
1480 QDBusAdaptorConnector *connector;
1481 if (node.flags & QDBusConnection::ExportAdaptors &&
1482 (connector = qDBusFindAdaptorConnector(node.obj))) {
1483 auto newflags = node.flags | QDBusConnection::ExportAllSlots;
1484
1485 if (msg.interface().isEmpty()) {
1486 // place the call in all interfaces
1487 // let the first one that handles it to work
1488 for (const QDBusAdaptorConnector::AdaptorData &adaptorData :
1489 std::as_const(connector->adaptors)) {
1490 if (activateCall(adaptorData.adaptor, newflags, msg))
1491 return;
1492 }
1493 } else {
1494 // check if we have an interface matching the name that was asked:
1495 QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
1496 it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
1497 msg.interface());
1498 if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1StringView(it->interface)) {
1499 if (!activateCall(it->adaptor, newflags, msg))
1500 sendError(msg, QDBusError::UnknownMethod);
1501 return;
1502 }
1503 }
1504 }
1505
1506 // no adaptors matched or were exported
1507 // try our standard filters
1508 if (activateInternalFilters(node, msg))
1509 return; // internal filters have already run or an error has been sent
1510
1511 // try the object itself:
1512 if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots) ||
1513 node.flags & (QDBusConnection::ExportScriptableInvokables|QDBusConnection::ExportNonScriptableInvokables)) {
1514 bool interfaceFound = true;
1515 if (!msg.interface().isEmpty()) {
1516 if (!node.interfaceName.isEmpty())
1517 interfaceFound = msg.interface() == node.interfaceName;
1518 else
1519 interfaceFound = qDBusInterfaceInObject(node.obj, msg.interface());
1520 }
1521
1522 if (interfaceFound) {
1523 if (!activateCall(node.obj, node.flags, msg))
1524 sendError(msg, QDBusError::UnknownMethod);
1525 return;
1526 }
1527 }
1528
1529 // nothing matched, send an error code
1530 if (msg.interface().isEmpty())
1531 sendError(msg, QDBusError::UnknownMethod);
1532 else
1533 sendError(msg, QDBusError::UnknownInterface);
1534}
1535
1536void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1537{
1538 // if the msg is external, we were called from inside doDispatch
1539 // that means the dispatchLock mutex is locked
1540 // must not call out to user code in that case
1541 //
1542 // however, if the message is internal, handleMessage was called directly
1543 // (user's thread) and no lock is in place. We can therefore call out to
1544 // user code, if necessary.
1545 ObjectTreeNode result;
1546 int usedLength;
1547 QThread *objThread = nullptr;
1548 QLatch latch(1);
1549 bool latchWait;
1550
1551 {
1552 QDBusReadLocker locker(HandleObjectCallAction, this);
1553 if (!findObject(&rootNode, msg.path(), usedLength, result)) {
1554 // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1555 sendError(msg, QDBusError::UnknownObject);
1556 return;
1557 }
1558
1559 if (!result.obj) {
1560 // no object -> no threading issues
1561 // it's either going to be an error, or an internal filter
1562 activateObject(result, msg, usedLength);
1563 return;
1564 }
1565
1566 objThread = result.obj->thread();
1567 if (!objThread) {
1568 send(msg.createErrorReply(QDBusError::InternalError,
1569 "Object '%1' (at path '%2')"
1570 " has no thread. Cannot deliver message."_L1
1571 .arg(result.obj->objectName(), msg.path())));
1572 return;
1573 }
1574
1575 if (!QDBusMessagePrivate::isLocal(msg)) {
1576 // external incoming message
1577 // post it and forget
1578 postEventToThread(HandleObjectCallPostEventAction, result.obj,
1579 new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1580 usedLength, msg));
1581 return;
1582 } else if (objThread != QThread::currentThread()) {
1583 // looped-back message, targeting another thread:
1584 // synchronize with it
1585 postEventToThread(HandleObjectCallPostEventAction, result.obj,
1586 new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1587 usedLength, msg, &latch));
1588 latchWait = true;
1589 } else {
1590 // looped-back message, targeting current thread
1591 latchWait = false;
1592 }
1593 } // release the lock
1594
1595 if (latchWait)
1596 latch.wait();
1597 else
1598 activateObject(result, msg, usedLength);
1599}
1600
1602{
1603 if (!handled) {
1604 // we're being destroyed without delivering
1605 // it means the object was deleted between posting and delivering
1606 QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1607 that->sendError(message, QDBusError::UnknownObject);
1608 }
1609
1610 // semaphore releasing happens in ~QMetaCallEvent
1611}
1612
1614{
1615 QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1616
1619 that->activateObject(node, message, pathStartPos);
1622
1623 handled = true;
1624}
1625
1626void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1627{
1628 SignalHookHash::const_iterator it = signalHooks.constFind(key);
1629 SignalHookHash::const_iterator end = signalHooks.constEnd();
1630 //qDebug("looking for: %s", path.toLocal8Bit().constData());
1631 //qDBusDebug() << signalHooks.keys();
1632 for ( ; it != end && it.key() == key; ++it) {
1633 const SignalHook &hook = it.value();
1634 if (!hook.service.isEmpty()) {
1635 QString owner = watchedServices.value(hook.service, WatchedServiceData(hook.service)).owner;
1636 if (owner != msg.service())
1637 continue;
1638 }
1639 if (!hook.path.isEmpty() && hook.path != msg.path())
1640 continue;
1641 if (!hook.signature.isEmpty() && hook.signature != msg.signature())
1642 continue;
1643 if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1644 continue;
1645 if (!hook.argumentMatch.args.isEmpty()) {
1646 const QVariantList arguments = msg.arguments();
1647 if (hook.argumentMatch.args.size() > arguments.size())
1648 continue;
1649
1650 bool matched = true;
1651 for (int i = 0; i < hook.argumentMatch.args.size(); ++i) {
1652 const QString &param = hook.argumentMatch.args.at(i);
1653 if (param.isNull())
1654 continue; // don't try to match against this
1655 if (param == arguments.at(i).toString())
1656 continue; // matched
1657 matched = false;
1658 break;
1659 }
1660 if (!matched)
1661 continue;
1662 }
1663 if (!hook.argumentMatch.arg0namespace.isEmpty()) {
1664 const QVariantList arguments = msg.arguments();
1665 if (arguments.size() < 1)
1666 continue;
1667 const QString param = arguments.at(0).toString();
1668 const QStringView ns = hook.argumentMatch.arg0namespace;
1669 if (!param.startsWith(ns) || (param.size() != ns.size() && param[ns.size()] != u'.'))
1670 continue;
1671 }
1672 activateSignal(hook, msg);
1673 }
1674}
1675
1676void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1677{
1678 // We call handlesignal(QString, QDBusMessage) three times:
1679 // one with member:interface
1680 // one with member:
1681 // one with :interface
1682 // This allows us to match signals with wildcards on member or interface
1683 // (but not both)
1684
1685 QString key = msg.member();
1686 key.reserve(key.size() + 1 + msg.interface().size());
1687 key += u':';
1688 key += msg.interface();
1689
1690 QDBusWriteLocker locker(HandleSignalAction, this);
1691 handleSignal(key, msg); // one try
1692
1693 key.truncate(msg.member().size() + 1); // keep the ':'
1694 handleSignal(key, msg); // second try
1695
1696 key = u':';
1697 key += msg.interface();
1698 handleSignal(key, msg); // third try
1699}
1700
1701void QDBusConnectionPrivate::watchForDBusDisconnection()
1702{
1703 SignalHook hook;
1704 // Initialize the hook for Disconnected signal
1705 hook.service.clear(); // org.freedesktop.DBus.Local.Disconnected uses empty service name
1706 hook.path = QDBusUtil::dbusPathLocal();
1707 hook.obj = this;
1708 hook.params << QMetaType(QMetaType::Void);
1709 hook.midx = staticMetaObject.indexOfSlot("handleDBusDisconnection()");
1710 Q_ASSERT(hook.midx != -1);
1711 signalHooks.insert("Disconnected:" DBUS_INTERFACE_LOCAL ""_L1, hook);
1712}
1713
1714void QDBusConnectionPrivate::setServer(QDBusServer *object, DBusServer *s, const QDBusErrorInternal &error)
1715{
1716 mode = ServerMode;
1717 serverObject = object;
1718 object->d = this;
1719 if (!s) {
1720 handleError(error);
1721 return;
1722 }
1723
1724 server = s;
1725
1726 dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot);
1727 if (data_allocated && server_slot < 0)
1728 return;
1729
1730 dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server,
1731 qDBusAddWatch,
1732 qDBusRemoveWatch,
1733 qDBusToggleWatch,
1734 this, nullptr);
1735 //qDebug() << "watch_functions_set" << watch_functions_set;
1736 Q_UNUSED(watch_functions_set);
1737
1738 dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server,
1739 qDBusAddTimeout,
1740 qDBusRemoveTimeout,
1741 qDBusToggleTimeout,
1742 this, nullptr);
1743 //qDebug() << "time_functions_set" << time_functions_set;
1744 Q_UNUSED(time_functions_set);
1745
1746 q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, nullptr);
1747
1748 dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, nullptr);
1749 //qDebug() << "data_set" << data_set;
1750 Q_UNUSED(data_set);
1751}
1752
1753void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error)
1754{
1755 mode = PeerMode;
1756 if (!c) {
1757 handleError(error);
1758 return;
1759 }
1760
1761 connection = c;
1762
1763 q_dbus_connection_set_exit_on_disconnect(connection, false);
1764 q_dbus_connection_set_watch_functions(connection,
1765 qDBusAddWatch,
1766 qDBusRemoveWatch,
1767 qDBusToggleWatch,
1768 this, nullptr);
1769 q_dbus_connection_set_timeout_functions(connection,
1770 qDBusAddTimeout,
1771 qDBusRemoveTimeout,
1772 qDBusToggleTimeout,
1773 this, nullptr);
1774 q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, nullptr);
1775 q_dbus_connection_add_filter(connection,
1776 qDBusSignalFilter,
1777 this, nullptr);
1778
1779 watchForDBusDisconnection();
1780
1781 QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection);
1782}
1783
1785{
1786 QDBusConnection::ConnectionCapabilities result;
1787 typedef dbus_bool_t (*can_send_type_t)(DBusConnection *, int);
1788 static can_send_type_t can_send_type = nullptr;
1789
1790#if defined(QT_LINKED_LIBDBUS)
1791# if DBUS_VERSION-0 >= 0x010400
1792 can_send_type = dbus_connection_can_send_type;
1793# endif
1794#elif QT_CONFIG(library)
1795 // run-time check if the next functions are available
1796 can_send_type = (can_send_type_t)qdbus_resolve_conditionally("dbus_connection_can_send_type");
1797#endif
1798
1799#ifndef DBUS_TYPE_UNIX_FD
1800# define DBUS_TYPE_UNIX_FD int('h')
1801#endif
1802 if (can_send_type && can_send_type(connection, DBUS_TYPE_UNIX_FD))
1803 result |= QDBusConnection::UnixFileDescriptorPassing;
1804
1805 return result;
1806}
1807
1808void QDBusConnectionPrivate::handleAuthentication()
1809{
1810 capabilities.storeRelaxed(::connectionCapabilities(connection));
1811 isAuthenticated = true;
1812}
1813
1814void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error)
1815{
1816 mode = ClientMode;
1817 if (!dbc) {
1818 handleError(error);
1819 return;
1820 }
1821
1822 connection = dbc;
1823
1824 const char *service = q_dbus_bus_get_unique_name(connection);
1825 Q_ASSERT(service);
1826 baseService = QString::fromUtf8(service);
1827 // bus connections are already authenticated here because q_dbus_bus_register() has been called
1828 handleAuthentication();
1829
1830 q_dbus_connection_set_exit_on_disconnect(connection, false);
1831 q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
1832 qDBusToggleWatch, this, nullptr);
1833 q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
1834 qDBusToggleTimeout, this, nullptr);
1835 q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, nullptr);
1836 q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, nullptr);
1837
1838 // Initialize the hooks for the NameAcquired and NameLost signals
1839 // we don't use connectSignal here because we don't need the rules to be sent to the bus
1840 // the bus will always send us these two signals
1841 SignalHook hook;
1842 hook.service = QDBusUtil::dbusService();
1843 hook.path.clear(); // no matching
1844 hook.obj = this;
1845 hook.params << QMetaType(QMetaType::Void) << QMetaType(QMetaType::QString); // both functions take a QString as parameter and return void
1846
1847 hook.midx = staticMetaObject.indexOfSlot("registerServiceNoLock(QString)");
1848 Q_ASSERT(hook.midx != -1);
1849 signalHooks.insert("NameAcquired:" DBUS_INTERFACE_DBUS ""_L1, hook);
1850
1851 hook.midx = staticMetaObject.indexOfSlot("unregisterServiceNoLock(QString)");
1852 Q_ASSERT(hook.midx != -1);
1853 signalHooks.insert("NameLost:" DBUS_INTERFACE_DBUS ""_L1, hook);
1854
1855 // And initialize the hook for the NameOwnerChanged signal;
1856 // we don't use connectSignal here because the rules are added by connectSignal on a per-need basis
1857 hook.params.clear();
1858 hook.params.reserve(4);
1859 hook.params << QMetaType(QMetaType::Void) << QMetaType(QMetaType::QString) << QMetaType(QMetaType::QString) << QMetaType(QMetaType::QString);
1860 hook.midx = staticMetaObject.indexOfSlot("serviceOwnerChangedNoLock(QString,QString,QString)");
1861 Q_ASSERT(hook.midx != -1);
1862 signalHooks.insert("NameOwnerChanged:" DBUS_INTERFACE_DBUS ""_L1, hook);
1863
1864 watchForDBusDisconnection();
1865
1866 qDBusDebug() << this << ": connected successfully";
1867
1868 // schedule a dispatch:
1869 QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection);
1870}
1871
1872extern "C"{
1873static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1874{
1875 QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data);
1876 Q_ASSERT(call->pending == pending);
1877 QDBusConnectionPrivate::processFinishedCall(call);
1878}
1879}
1880
1881void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
1882{
1883 QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1884
1885 auto locker = qt_unique_lock(call->mutex);
1886
1887 connection->pendingCalls.removeOne(call);
1888
1889 QDBusMessage &msg = call->replyMessage;
1890 if (call->pending) {
1891 // when processFinishedCall is called and pending call is not completed,
1892 // it means we received disconnected signal from libdbus
1893 if (q_dbus_pending_call_get_completed(call->pending)) {
1894 // decode the message
1895 DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
1896 msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->connectionCapabilities());
1897 q_dbus_message_unref(reply);
1898 } else {
1899 msg = QDBusMessage::createError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
1900 }
1901 }
1902 qDBusDebug() << connection << "got message reply:" << msg;
1903
1904 // Check if the reply has the expected signature
1905 call->checkReceivedSignature();
1906
1907 if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) {
1908 // Deliver the return values of a remote function call.
1909 //
1910 // There is only one connection and it is specified by idx
1911 // The slot must have the same parameter types that the message does
1912 // The slot may have less parameters than the message
1913 // The slot may optionally have one final parameter that is QDBusMessage
1914 // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
1915
1916 QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx,
1917 call->metaTypes, msg);
1918 if (e)
1919 connection->postEventToThread(MessageResultReceivedAction, call->receiver, e);
1920 else
1921 qDBusDebug("Deliver failed!");
1922 }
1923
1924 if (call->pending) {
1925 q_dbus_pending_call_unref(call->pending);
1926 call->pending = nullptr;
1927 }
1928
1929 // Are there any watchers?
1930 if (call->watcherHelper)
1931 call->watcherHelper->emitSignals(msg, call->sentMessage);
1932
1933 call->waitForFinishedCondition.wakeAll();
1934 locker.unlock();
1935
1936 if (msg.type() == QDBusMessage::ErrorMessage)
1937 emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage);
1938
1939 if (!call->ref.deref())
1940 delete call;
1941}
1942
1943bool QDBusConnectionPrivate::send(const QDBusMessage& message)
1944{
1945 if (QDBusMessagePrivate::isLocal(message))
1946 return true; // don't send; the reply will be retrieved by the caller
1947 // through the d_ptr->localReply link
1948
1949 QDBusError error;
1950 DBusMessage *msg =
1951 QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
1952 if (!msg) {
1953 if (message.type() == QDBusMessage::MethodCallMessage)
1954 qCWarning(dbusIntegration,
1955 "QDBusConnection: error: could not send message to service \"%s\" path "
1956 "\"%s\" interface \"%s\" member \"%s\": %s",
1957 qPrintable(message.service()), qPrintable(message.path()),
1958 qPrintable(message.interface()), qPrintable(message.member()),
1959 qPrintable(error.message()));
1960 else if (message.type() == QDBusMessage::SignalMessage)
1961 qCWarning(dbusIntegration,
1962 "QDBusConnection: error: could not send signal to service \"%s\" path \"%s\" "
1963 "interface \"%s\" member \"%s\": %s",
1964 qPrintable(message.service()), qPrintable(message.path()),
1965 qPrintable(message.interface()), qPrintable(message.member()),
1966 qPrintable(error.message()));
1967 else
1968 qCWarning(dbusIntegration,
1969 "QDBusConnection: error: could not send %s message to service \"%s\": %s",
1970 message.type() == QDBusMessage::ReplyMessage ? "reply"
1971 : message.type() == QDBusMessage::ErrorMessage ? "error"
1972 : "invalid",
1973 qPrintable(message.service()), qPrintable(error.message()));
1974 lastError = error;
1975 return false;
1976 }
1977
1978 q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1979 qDBusDebug() << this << "sending message (no reply):" << message;
1980 emit messageNeedsSending(nullptr, msg);
1981 return true;
1982}
1983
1984// small helper to note long running blocking dbus calls.
1985// these are generally a sign of fragile software (too long a call can either
1986// lead to bad user experience, if it's running on the GUI thread for instance)
1987// or break completely under load (hitting the call timeout).
1988//
1989// as a result, this is something we want to watch for.
1991{
1992public:
1995 {
1996#if defined(QT_NO_DEBUG)
1997 // when in a release build, we default these to off.
1998 // this means that we only affect code that explicitly enables the warning.
1999 Q_CONSTINIT static int mainThreadWarningAmount = -1;
2000 Q_CONSTINIT static int otherThreadWarningAmount = -1;
2001#else
2002 Q_CONSTINIT static int mainThreadWarningAmount = 200;
2003 Q_CONSTINIT static int otherThreadWarningAmount = 500;
2004#endif
2005 Q_CONSTINIT static bool initializedAmounts = false;
2008
2009 if (!initializedAmounts) {
2010 int tmp = 0;
2012 bool ok = true;
2013
2014 env = qgetenv("Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS");
2015 if (!env.isEmpty()) {
2016 tmp = env.toInt(&ok);
2017 if (ok)
2019 else
2020 qCWarning(
2022 "QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS "
2023 "must be an integer; value ignored");
2024 }
2025
2026 env = qgetenv("Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS");
2027 if (!env.isEmpty()) {
2028 tmp = env.toInt(&ok);
2029 if (ok)
2031 else
2033 "QDBusBlockingCallWatcher: "
2034 "Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS must be an integer; "
2035 "value ignored");
2036 }
2037
2038 initializedAmounts = true;
2039 }
2040
2041 locker.unlock();
2042
2043 // if this call is running on the main thread, we have a much lower
2044 // tolerance for delay because any long-term delay will wreck user
2045 // interactivity.
2046 if (QThread::isMainThread())
2048 else
2050
2052 }
2053
2055 {
2056 if (m_maxCallTimeoutMs < 0)
2057 return; // disabled
2058
2059 if (m_callTimer.elapsed() >= m_maxCallTimeoutMs) {
2060 qCWarning(
2061 dbusIntegration,
2062 "QDBusConnection: warning: blocking call took a long time (%d ms, max for this "
2063 "thread is %d ms) to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"",
2064 int(m_callTimer.elapsed()), m_maxCallTimeoutMs, qPrintable(m_message.service()),
2065 qPrintable(m_message.path()), qPrintable(m_message.interface()),
2066 qPrintable(m_message.member()));
2067 }
2068 }
2069
2070private:
2071 QDBusMessage m_message;
2072 int m_maxCallTimeoutMs;
2073 QElapsedTimer m_callTimer;
2074};
2075
2076QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
2077 QDBus::CallMode mode, int timeout)
2078{
2079 QDBusBlockingCallWatcher watcher(message);
2080
2081 QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, nullptr, nullptr, nullptr, timeout);
2082 Q_ASSERT(pcall);
2083
2084 if (mode == QDBus::BlockWithGui)
2085 pcall->waitForFinishedWithGui();
2086 else
2087 pcall->waitForFinished();
2088
2089 QDBusMessage reply = pcall->replyMessage;
2090 lastError = QDBusError(reply); // set or clear error
2091
2092 if (!pcall->ref.deref())
2093 delete pcall;
2094 return reply;
2095}
2096
2097QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message)
2098{
2099 qDBusDebug() << this << "sending message via local-loop:" << message;
2100
2101 QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message);
2102 bool handled = handleMessage(localCallMsg);
2103
2104 if (!handled) {
2105 QString interface = message.interface();
2106 if (interface.isEmpty())
2107 interface = "<no-interface>"_L1;
2108 return QDBusMessage::createError(QDBusError::InternalError,
2109 "Internal error trying to call %1.%2 at %3 (signature '%4'"_L1
2110 .arg(interface, message.member(),
2111 message.path(), message.signature()));
2112 }
2113
2114 // if the message was handled, there might be a reply
2115 QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg);
2116 if (localReplyMsg.type() == QDBusMessage::InvalidMessage) {
2117 qCWarning(
2118 dbusIntegration,
2119 "QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') "
2120 "on blocking mode",
2121 qPrintable(message.member()), qPrintable(message.path()),
2122 qPrintable(message.signature()));
2123 return QDBusMessage::createError(
2124 QDBusError(QDBusError::InternalError,
2125 "local-loop message cannot have delayed replies"_L1));
2126 }
2127
2128 // there is a reply
2129 qDBusDebug() << this << "got message via local-loop:" << localReplyMsg;
2130 return localReplyMsg;
2131}
2132
2133QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message,
2134 QObject *receiver, const char *returnMethod,
2135 const char *errorMethod, int timeout)
2136{
2137 QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
2138 bool isLoopback;
2139 if ((isLoopback = isServiceRegisteredByThread(message.service()))) {
2140 // special case for local calls
2141 pcall->replyMessage = sendWithReplyLocal(message);
2142 }
2143
2144 if (receiver && returnMethod)
2145 pcall->setReplyCallback(receiver, returnMethod);
2146
2147 if (errorMethod) {
2148 Q_ASSERT(!pcall->watcherHelper);
2149 pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
2150 connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod,
2151 Qt::QueuedConnection);
2152 pcall->watcherHelper->moveToThread(thread());
2153 }
2154
2155 if ((receiver && returnMethod) || errorMethod) {
2156 // no one waiting, will delete pcall in processFinishedCall()
2157 pcall->ref.storeRelaxed(1);
2158 } else {
2159 // set double ref to prevent race between processFinishedCall() and ref counting
2160 // by QDBusPendingCall::QExplicitlySharedDataPointer<QDBusPendingCallPrivate>
2161 pcall->ref.storeRelaxed(2);
2162 }
2163
2164 if (isLoopback) {
2165 // a loopback call
2166 processFinishedCall(pcall);
2167 return pcall;
2168 }
2169
2170 QDBusError error;
2171 DBusMessage *msg =
2172 QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
2173 if (!msg) {
2174 qCWarning(dbusIntegration,
2175 "QDBusConnection: error: could not send message to service \"%s\" path \"%s\" "
2176 "interface \"%s\" member \"%s\": %s",
2177 qPrintable(message.service()), qPrintable(message.path()),
2178 qPrintable(message.interface()), qPrintable(message.member()),
2179 qPrintable(error.message()));
2180 pcall->replyMessage = QDBusMessage::createError(error);
2181 lastError = error;
2182 processFinishedCall(pcall);
2183 } else {
2184 qDBusDebug() << this << "sending message:" << message;
2185 emit messageNeedsSending(pcall, msg, timeout);
2186 }
2187 return pcall;
2188}
2189
2190void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void *message, int timeout)
2191{
2192 QDBusError error;
2193 DBusPendingCall *pending = nullptr;
2194 DBusMessage *msg = static_cast<DBusMessage *>(message);
2195 bool isNoReply = !pcall;
2196 Q_ASSERT(isNoReply == !!q_dbus_message_get_no_reply(msg));
2197
2198 checkThread();
2199
2200 if (isNoReply && q_dbus_connection_send(connection, msg, nullptr)) {
2201 // success
2202 } else if (!isNoReply && q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
2203 if (pending) {
2204 q_dbus_message_unref(msg);
2205
2206 pcall->pending = pending;
2207 q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, nullptr);
2208
2209 // DBus won't notify us when a peer disconnects or server terminates so we need to track these ourselves
2210 if (mode == QDBusConnectionPrivate::PeerMode || mode == QDBusConnectionPrivate::ClientMode)
2211 pendingCalls.append(pcall);
2212
2213 return;
2214 } else {
2215 // we're probably disconnected at this point
2216 lastError = error = QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
2217 }
2218 } else {
2219 lastError = error = QDBusError(QDBusError::NoMemory, QStringLiteral("Out of memory"));
2220 }
2221
2222 q_dbus_message_unref(msg);
2223 if (pcall) {
2224 pcall->replyMessage = QDBusMessage::createError(error);
2225 processFinishedCall(pcall);
2226 }
2227}
2228
2229
2230bool QDBusConnectionPrivate::connectSignal(const QString &service,
2231 const QString &path, const QString &interface, const QString &name,
2232 const QStringList &argumentMatch, const QString &signature,
2233 QObject *receiver, const char *slot)
2234{
2235 ArgMatchRules rules;
2236 rules.args = argumentMatch;
2237 return connectSignal(service, path, interface, name, rules, signature, receiver, slot);
2238}
2239
2240bool QDBusConnectionPrivate::connectSignal(const QString &service,
2241 const QString &path, const QString &interface, const QString &name,
2242 const ArgMatchRules &argumentMatch, const QString &signature,
2243 QObject *receiver, const char *slot)
2244{
2245 // check the slot
2246 QDBusConnectionPrivate::SignalHook hook;
2247 QString key;
2248
2249 hook.signature = signature;
2250 QString errorMsg;
2251 if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0,
2252 false, errorMsg)) {
2253 qCWarning(dbusIntegration)
2254 << "Could not connect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg);
2255 return false; // don't connect
2256 }
2257
2258 Q_ASSERT(thread() != QThread::currentThread());
2259 return addSignalHook(key, hook);
2260}
2261
2262bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook &hook)
2263{
2264 bool result = false;
2265
2266 QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::addSignalHookImpl,
2267 Qt::BlockingQueuedConnection, qReturnArg(result), key, hook);
2268
2269 return result;
2270}
2271
2272bool QDBusConnectionPrivate::addSignalHookImpl(const QString &key, const SignalHook &hook)
2273{
2274 QDBusWriteLocker locker(ConnectAction, this);
2275
2276 // avoid duplicating:
2277 QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.constFind(key);
2278 QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd();
2279 for ( ; it != end && it.key() == key; ++it) {
2280 const QDBusConnectionPrivate::SignalHook &entry = it.value();
2281 if (entry.service == hook.service &&
2282 entry.path == hook.path &&
2283 entry.signature == hook.signature &&
2284 entry.obj == hook.obj &&
2285 entry.midx == hook.midx &&
2286 entry.argumentMatch == hook.argumentMatch) {
2287 // no need to compare the parameters if it's the same slot
2288 return false; // already there
2289 }
2290 }
2291
2292 signalHooks.insert(key, hook);
2293 connect(hook.obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed,
2294 Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2295
2296 MatchRefCountHash::iterator mit = matchRefCounts.find(hook.matchRule);
2297
2298 if (mit != matchRefCounts.end()) { // Match already present
2299 mit.value() = mit.value() + 1;
2300 return true;
2301 }
2302
2303 matchRefCounts.insert(hook.matchRule, 1);
2304
2305 if (connection) {
2306 if (mode != QDBusConnectionPrivate::PeerMode) {
2307 qDBusDebug() << this << "Adding rule:" << hook.matchRule;
2308 q_dbus_bus_add_match(connection, hook.matchRule, nullptr);
2309
2310 // Successfully connected the signal
2311 // Do we need to watch for this name?
2312 if (shouldWatchService(hook.service)) {
2313 WatchedServicesHash::mapped_type &data = watchedServices[hook.service];
2314 if (++data.refcount == 1) {
2315 // we need to watch for this service changing
2316 ArgMatchRules rules;
2317 rules.args << hook.service;
2318 q_dbus_bus_add_match(connection,
2319 buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(),
2320 QDBusUtil::nameOwnerChanged(), rules, QString()),
2321 nullptr);
2322 data.owner = getNameOwnerNoCache(hook.service);
2323 qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:"
2324 << data.owner << ")";
2325 }
2326 }
2327 }
2328 }
2329 return true;
2330}
2331
2332bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
2333 const QString &path, const QString &interface, const QString &name,
2334 const QStringList &argumentMatch, const QString &signature,
2335 QObject *receiver, const char *slot)
2336{
2337 ArgMatchRules rules;
2338 rules.args = argumentMatch;
2339 return disconnectSignal(service, path, interface, name, rules, signature, receiver, slot);
2340}
2341
2342bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
2343 const QString &path, const QString &interface, const QString &name,
2344 const ArgMatchRules &argumentMatch, const QString &signature,
2345 QObject *receiver, const char *slot)
2346{
2347 // check the slot
2348 QDBusConnectionPrivate::SignalHook hook;
2349 QString key;
2350 QString name2 = name;
2351 if (name2.isNull())
2352 name2.detach();
2353
2354 hook.signature = signature;
2355 QString errorMsg;
2356 if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0,
2357 false, errorMsg)) {
2358 qCWarning(dbusIntegration)
2359 << "Could not disconnect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg);
2360 return false; // don't disconnect
2361 }
2362
2363 Q_ASSERT(thread() != QThread::currentThread());
2364 return removeSignalHook(key, hook);
2365}
2366
2367bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHook &hook)
2368{
2369 bool result = false;
2370
2371 QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::removeSignalHookImpl,
2372 Qt::BlockingQueuedConnection, qReturnArg(result), key, hook);
2373
2374 return result;
2375}
2376
2377bool QDBusConnectionPrivate::removeSignalHookImpl(const QString &key, const SignalHook &hook)
2378{
2379 // remove it from our list:
2380 QDBusWriteLocker locker(ConnectAction, this);
2381 QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key);
2382 QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end();
2383 for ( ; it != end && it.key() == key; ++it) {
2384 const QDBusConnectionPrivate::SignalHook &entry = it.value();
2385 if (entry.service == hook.service &&
2386 entry.path == hook.path &&
2387 entry.signature == hook.signature &&
2388 entry.obj == hook.obj &&
2389 entry.midx == hook.midx &&
2390 entry.argumentMatch.args == hook.argumentMatch.args) {
2391 // no need to compare the parameters if it's the same slot
2392 removeSignalHookNoLock(it);
2393 return true; // it was there
2394 }
2395 }
2396
2397 // the slot was not found
2398 return false;
2399}
2400
2401QDBusConnectionPrivate::SignalHookHash::Iterator
2402QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it)
2403{
2404 const SignalHook &hook = it.value();
2405
2406 bool erase = false;
2407 MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule);
2408 if (i == matchRefCounts.end()) {
2409 qCWarning(dbusIntegration,
2410 "QDBusConnectionPrivate::disconnectSignal: MatchRule not found in "
2411 "matchRefCounts!!");
2412 } else {
2413 if (i.value() == 1) {
2414 erase = true;
2415 matchRefCounts.erase(i);
2416 }
2417 else {
2418 i.value() = i.value() - 1;
2419 }
2420 }
2421
2422 // we don't care about errors here
2423 if (connection && erase) {
2424 if (mode != QDBusConnectionPrivate::PeerMode) {
2425 qDBusDebug() << this << "Removing rule:" << hook.matchRule;
2426 q_dbus_bus_remove_match(connection, hook.matchRule, nullptr);
2427
2428 // Successfully disconnected the signal
2429 // Were we watching for this name?
2430 WatchedServicesHash::Iterator sit = watchedServices.find(hook.service);
2431 if (sit != watchedServices.end()) {
2432 if (--sit.value().refcount == 0) {
2433 watchedServices.erase(sit);
2434 ArgMatchRules rules;
2435 rules.args << hook.service;
2436 q_dbus_bus_remove_match(connection,
2437 buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(),
2438 QDBusUtil::nameOwnerChanged(), rules, QString()),
2439 nullptr);
2440 }
2441 }
2442 }
2443
2444 }
2445
2446 return signalHooks.erase(it);
2447}
2448
2449void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
2450{
2451 connect(node->obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed,
2452 Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2453
2454 if (node->flags & (QDBusConnection::ExportAdaptors
2455 | QDBusConnection::ExportScriptableSignals
2456 | QDBusConnection::ExportNonScriptableSignals)) {
2457 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
2458
2459 if (node->flags & (QDBusConnection::ExportScriptableSignals
2460 | QDBusConnection::ExportNonScriptableSignals)) {
2461 connector->disconnectAllSignals(node->obj);
2462 connector->connectAllSignals(node->obj);
2463 }
2464
2465 connect(connector, &QDBusAdaptorConnector::relaySignal, this,
2466 &QDBusConnectionPrivate::relaySignal,
2467 Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
2468 }
2469}
2470
2471void QDBusConnectionPrivate::unregisterObject(const QString &path, QDBusConnection::UnregisterMode mode)
2472{
2473 QDBusConnectionPrivate::ObjectTreeNode *node = &rootNode;
2474 QList<QStringView> pathComponents;
2475 int i;
2476 if (path == "/"_L1) {
2477 i = 0;
2478 } else {
2479 pathComponents = QStringView{path}.split(u'/');
2480 i = 1;
2481 }
2482
2483 huntAndUnregister(pathComponents, i, mode, node);
2484}
2485
2486void QDBusConnectionPrivate::connectRelay(const QString &service,
2487 const QString &path, const QString &interface,
2488 QDBusAbstractInterface *receiver,
2489 const QMetaMethod &signal)
2490{
2491 // this function is called by QDBusAbstractInterface when one of its signals is connected
2492 // we set up a relay from D-Bus into it
2493 SignalHook hook;
2494 QString key;
2495
2496 QByteArray sig;
2497 sig.append(QSIGNAL_CODE + '0');
2498 sig.append(signal.methodSignature());
2499 QString errorMsg;
2500 if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig,
2501 QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) {
2502 qCWarning(dbusIntegration)
2503 << "Could not connect" << interface << "to" << signal.name() << ":" << qPrintable(errorMsg);
2504 return; // don't connect
2505 }
2506
2507 Q_ASSERT(thread() != QThread::currentThread());
2508 addSignalHook(key, hook);
2509}
2510
2511void QDBusConnectionPrivate::disconnectRelay(const QString &service,
2512 const QString &path, const QString &interface,
2513 QDBusAbstractInterface *receiver,
2514 const QMetaMethod &signal)
2515{
2516 // this function is called by QDBusAbstractInterface when one of its signals is disconnected
2517 // we remove relay from D-Bus into it
2518 SignalHook hook;
2519 QString key;
2520
2521 QByteArray sig;
2522 sig.append(QSIGNAL_CODE + '0');
2523 sig.append(signal.methodSignature());
2524 QString errorMsg;
2525 if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig,
2526 QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) {
2527 qCWarning(dbusIntegration) << "Could not disconnect" << interface << "to"
2528 << signal.methodSignature() << ":" << qPrintable(errorMsg);
2529 return; // don't disconnect
2530 }
2531
2532 Q_ASSERT(thread() != QThread::currentThread());
2533 removeSignalHook(key, hook);
2534}
2535
2536bool QDBusConnectionPrivate::shouldWatchService(const QString &service)
2537{
2538 // we don't have to watch anything in peer mode
2539 if (mode != ClientMode)
2540 return false;
2541 // we don't have to watch wildcard services (empty strings)
2542 if (service.isEmpty())
2543 return false;
2544 // we don't have to watch the bus driver
2545 if (service == QDBusUtil::dbusService())
2546 return false;
2547 return true;
2548}
2549
2550/*!
2551 Sets up a watch rule for service \a service for the change described by
2552 mode \a mode. When the change happens, slot \a member in object \a obj will
2553 be called.
2554
2555 The caller should call QDBusConnectionPrivate::shouldWatchService() before
2556 calling this function to check whether the service needs to be watched at
2557 all. Failing to do so may add rules that are never activated.
2558*/
2559void QDBusConnectionPrivate::watchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member)
2560{
2561 ArgMatchRules matchArgs = matchArgsForService(service, mode);
2562 connectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(),
2563 matchArgs, QString(), obj, member);
2564}
2565
2566/*!
2567 Removes a watch rule set up by QDBusConnectionPrivate::watchService(). The
2568 arguments to this function must be the same as the ones for that function.
2569
2570 Sets up a watch rule for service \a service for the change described by
2571 mode \a mode. When the change happens, slot \a member in object \a obj will
2572 be called.
2573*/
2574void QDBusConnectionPrivate::unwatchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member)
2575{
2576 ArgMatchRules matchArgs = matchArgsForService(service, mode);
2577 disconnectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(),
2578 matchArgs, QString(), obj, member);
2579}
2580
2581QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName)
2582{
2583 if (QDBusUtil::isValidUniqueConnectionName(serviceName))
2584 return serviceName;
2585 if (!connection)
2586 return QString();
2587
2588 {
2589 // acquire a read lock for the cache
2590 QReadLocker locker(&lock);
2591 WatchedServicesHash::ConstIterator it = watchedServices.constFind(serviceName);
2592 if (it != watchedServices.constEnd())
2593 return it->owner;
2594 }
2595
2596 // not cached
2597 return getNameOwnerNoCache(serviceName);
2598}
2599
2600QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName)
2601{
2602 QDBusMessage msg = QDBusMessage::createMethodCall(QDBusUtil::dbusService(),
2603 QDBusUtil::dbusPath(), QDBusUtil::dbusInterface(),
2604 QStringLiteral("GetNameOwner"));
2605 QDBusMessagePrivate::setParametersValidated(msg, true);
2606 msg << serviceName;
2607
2608 QDBusPendingCallPrivate *pcall = sendWithReplyAsync(msg, nullptr, nullptr, nullptr);
2609 if (thread() == QThread::currentThread()) {
2610 // this function may be called in our own thread and
2611 // QDBusPendingCallPrivate::waitForFinished() would deadlock there
2612 q_dbus_pending_call_block(pcall->pending);
2613 }
2614 pcall->waitForFinished();
2615 msg = pcall->replyMessage;
2616
2617 if (!pcall->ref.deref())
2618 delete pcall;
2619
2620 if (msg.type() == QDBusMessage::ReplyMessage)
2621 return msg.arguments().at(0).toString();
2622 return QString();
2623}
2624
2625QDBusMetaObject *
2626QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
2627 const QString &interface, QDBusError &error)
2628{
2629 // service must be a unique connection name
2630 if (!interface.isEmpty()) {
2631 QDBusReadLocker locker(FindMetaObject1Action, this);
2632 QDBusMetaObject *mo = cachedMetaObjects.value(interface, nullptr);
2633 if (mo)
2634 return mo;
2635 }
2636 if (path.isEmpty()) {
2637 error = QDBusError(QDBusError::InvalidObjectPath, "Object path cannot be empty"_L1);
2638 lastError = error;
2639 return nullptr;
2640 }
2641
2642 // introspect the target object
2643 QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
2644 QDBusUtil::dbusInterfaceIntrospectable(),
2645 QStringLiteral("Introspect"));
2646 QDBusMessagePrivate::setParametersValidated(msg, true);
2647
2648 QDBusMessage reply = sendWithReply(msg, QDBus::Block);
2649
2650 // it doesn't exist yet, we have to create it
2651 QDBusWriteLocker locker(FindMetaObject2Action, this);
2652 QDBusMetaObject *mo = nullptr;
2653 if (!interface.isEmpty())
2654 mo = cachedMetaObjects.value(interface, nullptr);
2655 if (mo)
2656 // maybe it got created when we switched from read to write lock
2657 return mo;
2658
2659 QString xml;
2660 if (reply.type() == QDBusMessage::ReplyMessage) {
2661 if (reply.signature() == "s"_L1)
2662 // fetch the XML description
2663 xml = reply.arguments().at(0).toString();
2664 } else {
2665 error = QDBusError(reply);
2666 lastError = error;
2667 if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod)
2668 return nullptr; // error
2669 }
2670
2671 // release the lock and return
2672 QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml,
2673 cachedMetaObjects, error);
2674 lastError = error;
2675 return result;
2676}
2677
2678void QDBusConnectionPrivate::registerService(const QString &serviceName)
2679{
2680 QDBusWriteLocker locker(RegisterServiceAction, this);
2681 registerServiceNoLock(serviceName);
2682}
2683
2684void QDBusConnectionPrivate::registerServiceNoLock(const QString &serviceName)
2685{
2686 serviceNames.append(serviceName);
2687}
2688
2689void QDBusConnectionPrivate::unregisterService(const QString &serviceName)
2690{
2691 QDBusWriteLocker locker(UnregisterServiceAction, this);
2692 unregisterServiceNoLock(serviceName);
2693}
2694
2695void QDBusConnectionPrivate::unregisterServiceNoLock(const QString &serviceName)
2696{
2697 serviceNames.removeAll(serviceName);
2698}
2699
2700bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName)
2701{
2702 if (!serviceName.isEmpty() && serviceName == baseService)
2703 return true;
2704 if (serviceName == QDBusUtil::dbusService())
2705 return false;
2706
2707 QDBusReadLocker locker(UnregisterServiceAction, this);
2708 return serviceNames.contains(serviceName);
2709}
2710
2711void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev)
2712{
2713 QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this);
2714 QCoreApplication::postEvent(object, ev);
2715 QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this);
2716}
2717
2718/*
2719 * Enable dispatch of D-Bus events for this connection, but only after
2720 * context's thread's event loop has started and processed any already
2721 * pending events. The event dispatch is then enabled in the DBus aux thread.
2722 */
2723void QDBusConnectionPrivate::enableDispatchDelayed(QObject *context)
2724{
2725 ref.ref();
2726 QMetaObject::invokeMethod(
2727 context,
2728 [this]() {
2729 // This call cannot race with something disabling dispatch only
2730 // because dispatch is never re-disabled from Qt code on an
2731 // in-use connection once it has been enabled.
2732 QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::setDispatchEnabled,
2733 Qt::QueuedConnection, true);
2734 if (!ref.deref())
2735 deleteLater();
2736 },
2737 Qt::QueuedConnection);
2738}
2739
2740QT_END_NAMESPACE
2741
2742#endif // QT_NO_DBUS
void placeMetaCall(QObject *) override
static QDBusConnectionManager * instance()
~QDBusSpyCallEvent() override
void placeMetaCall(QObject *) override
static void invokeSpyHooks(const QDBusMessage &msg)
void(* Hook)(const QDBusMessage &)
void add(QDBusSpyCallEvent::Hook hook)
void invoke(const QDBusMessage &msg)
Definition qlist.h:81
@ DBUS_WATCH_READABLE
@ DBUS_WATCH_WRITABLE
#define DBUS_TYPE_UNIX_FD
dbus_uint32_t dbus_bool_t
qint32 dbus_int32_t
@ DBUS_HANDLER_RESULT_HANDLED
@ DBUS_HANDLER_RESULT_NOT_YET_HANDLED
#define DBUS_INTERFACE_LOCAL
#define DBUS_INTERFACE_DBUS
@ DBUS_DISPATCH_DATA_REMAINS
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return dbus_watch_get_unix_fd
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return dbus_connection_send
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction dbus_timeout_get_enabled
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return dbus_message_unref
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return dbus_message_copy
static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data)
#define qDBusDebug
static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode)
static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, int &usedLength, QDBusConnectionPrivate::ObjectTreeNode &result)
static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data)
static void qDBusRemoveWatch(DBusWatch *watch, void *data)
static dbus_int32_t server_slot
static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
static void huntAndUnregister(const QList< QStringView > &pathComponents, int i, QDBusConnection::UnregisterMode mode, QDBusConnectionPrivate::ObjectTreeNode *node)
static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
static QDBusConnection::ConnectionCapabilities connectionCapabilities(DBusConnection *connection)
static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
static QObject * findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, int start)
Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook)
static QDBusCallDeliveryEvent *const DIRECT_DELIVERY
static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, bool isScriptable, bool isAdaptor, const QString &path=QString())
static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
static QByteArray buildMatchRule(const QString &service, const QString &objectPath, const QString &interface, const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString &)
static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
static void qDBusToggleWatch(DBusWatch *watch, void *data)
static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, const QString &signature_, QList< QMetaType > &metaTypes)
static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
@ HandleObjectCallPostEventAction
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
QMutex QBasicMutex
Definition qmutex.h:360
#define qPrintable(string)
Definition qstring.h:1683
#define QStringLiteral(str)
Definition qstring.h:1825
#define qUtf16Printable(string)
Definition qstring.h:1695
static void reportThreadAction(int, int, QDBusConnectionPrivate *)