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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/dbus/qdbusintegrator.cpp