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

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