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