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