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