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