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