diff options
Diffstat (limited to 'src/dbus/qdbusintegrator.cpp')
-rw-r--r-- | src/dbus/qdbusintegrator.cpp | 513 |
1 files changed, 299 insertions, 214 deletions
diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index e9b4bd0b1d..836562f496 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -6,7 +6,7 @@ #include <qcoreapplication.h> #include <qelapsedtimer.h> -#include <qdebug.h> +#include <qloggingcategory.h> #include <qmetaobject.h> #include <qobject.h> #include <qsocketnotifier.h> @@ -50,7 +50,9 @@ QT_IMPL_METATYPE_EXTERN(QDBusSlotCache) // used with dbus_server_allocate_data_slot static dbus_int32_t server_slot = -1; -static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1); +Q_LOGGING_CATEGORY(dbusIntegration, "qt.dbus.integration", QtWarningMsg) + +Q_CONSTINIT static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1); #define qDBusDebug if (::isDebugging.loadRelaxed() == 0); else qDebug static inline QDebug operator<<(QDebug dbg, const QThread *th) @@ -99,7 +101,34 @@ void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate * qdbusThreadDebugFunc qdbusThreadDebug = nullptr; #endif -typedef QVarLengthArray<QDBusSpyCallEvent::Hook, 4> QDBusSpyHookList; +class QDBusSpyHookList +{ +public: + void add(QDBusSpyCallEvent::Hook hook) + { + const auto locker = qt_scoped_lock(lock); + list.append(hook); + } + + void invoke(const QDBusMessage &msg) + { + // Create a copy of the hook list here, so that the hooks can be called + // without holding the lock. + QList<QDBusSpyCallEvent::Hook> hookListCopy; + { + const auto locker = qt_scoped_lock(lock); + hookListCopy = list; + } + + for (auto hook : std::as_const(hookListCopy)) + hook(msg); + } + +private: + QBasicMutex lock; + QList<QDBusSpyCallEvent::Hook> list; +}; + Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) extern "C" { @@ -122,8 +151,8 @@ static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) Q_ASSERT(d->timeouts.key(timeout, 0) == 0); - int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout)); - Q_ASSERT_X(timerId, "QDBusConnection", "Failed to start a timer"); + using namespace std::chrono_literals; + int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout) * 1ms); // no overflow possible if (!timerId) return false; @@ -249,7 +278,6 @@ static void qDBusToggleWatch(DBusWatch *watch, void *data) static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) { Q_ASSERT(connection); - Q_UNUSED(connection); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); if (new_status == DBUS_DISPATCH_DATA_REMAINS) emit d->dispatchStatusChanged(); @@ -259,11 +287,11 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v { // ### We may want to separate the server from the QDBusConnectionPrivate Q_ASSERT(server); - Q_UNUSED(server); Q_ASSERT(connection); Q_ASSERT(data); - if (!QDBusConnectionManager::instance()) + auto *manager = QDBusConnectionManager::instance(); + if (!manager) return; // keep the connection alive @@ -274,35 +302,35 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v if (serverConnection->anonymousAuthenticationAllowed) q_dbus_connection_set_allow_anonymous(connection, true); - QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent()); - const auto locker = qt_scoped_lock(QDBusConnectionManager::instance()->mutex); - QDBusConnectionManager::instance()->setConnection("QDBusServer-"_L1 + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), newConnection); - serverConnection->serverConnectionNames << newConnection->name; + QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate; + + manager->addConnection( + "QDBusServer-"_L1 + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), + newConnection); + { + QWriteLocker locker(&serverConnection->lock); + serverConnection->serverConnectionNames << newConnection->name; + } // setPeer does the error handling for us QDBusErrorInternal error; newConnection->setPeer(connection, error); newConnection->setDispatchEnabled(false); + QReadLocker serverLock(&serverConnection->lock); + if (!serverConnection->serverObject) + return; + // this is a queued connection and will resume in the QDBusServer's thread - emit serverConnection->newServerConnection(newConnection); + QMetaObject::invokeMethod(serverConnection->serverObject, &QDBusServer::newConnection, + Qt::QueuedConnection, QDBusConnectionPrivate::q(newConnection)); // we've disabled dispatching of events, so now we post an event to the // QDBusServer's thread in order to enable it after the // QDBusServer::newConnection() signal has been received by the // application's code - newConnection->ref.ref(); - QReadLocker serverLock(&serverConnection->lock); - QDBusConnectionDispatchEnabler *o = new QDBusConnectionDispatchEnabler(newConnection); - QTimer::singleShot(0, o, SLOT(execute())); - if (serverConnection->serverObject) - o->moveToThread(serverConnection->serverObject->thread()); -} -void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection) -{ - Q_ASSERT(mode == ServerMode); - emit serverObject->newConnection(QDBusConnectionPrivate::q(newConnection)); + newConnection->enableDispatchDelayed(serverConnection->serverObject); } } // extern "C" @@ -327,7 +355,7 @@ static QByteArray buildMatchRule(const QString &service, // add the argument string-matching now if (!argMatch.args.isEmpty()) { const QString keyValue = "arg%1='%2',"_L1; - for (int i = 0; i < argMatch.args.count(); ++i) + for (int i = 0; i < argMatch.args.size(); ++i) if (!argMatch.args.at(i).isNull()) result += keyValue.arg(i).arg(argMatch.args.at(i)); } @@ -349,7 +377,7 @@ static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, return root; } int start = 0; - int length = fullpath.length(); + int length = fullpath.size(); if (fullpath.at(0) == u'/') start = 1; @@ -391,7 +419,7 @@ static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, int start) { - int length = fullpath.length(); + int length = fullpath.size(); // any object in the tree can tell us to switch to its own object tree: const QDBusConnectionPrivate::ObjectTreeNode *node = root; @@ -407,17 +435,14 @@ static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *ro pos = (pos == -1 ? length : pos); auto pathComponent = QStringView{fullpath}.mid(start, pos - start); - const QObjectList children = obj->children(); - // find a child with the proper name QObject *next = nullptr; - QObjectList::ConstIterator it = children.constBegin(); - QObjectList::ConstIterator end = children.constEnd(); - for ( ; it != end; ++it) - if ((*it)->objectName() == pathComponent) { - next = *it; + for (QObject *child : std::as_const(obj->children())) { + if (child->objectName() == pathComponent) { + next = child; break; } + } if (!next) break; @@ -460,7 +485,11 @@ static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString & extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook); void qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook) { - qDBusSpyHookList()->append(hook); + auto *hooks = qDBusSpyHookList(); + if (!hooks) + return; + + hooks->add(hook); } QDBusSpyCallEvent::~QDBusSpyCallEvent() @@ -475,14 +504,15 @@ QDBusSpyCallEvent::~QDBusSpyCallEvent() void QDBusSpyCallEvent::placeMetaCall(QObject *) { - invokeSpyHooks(msg, hooks, hookCount); + invokeSpyHooks(msg); } -inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg, const Hook *hooks, int hookCount) +inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg) { - // call the spy hook list - for (int i = 0; i < hookCount; ++i) - hooks[i](msg); + if (!qDBusSpyHookList.exists()) + return; + + qDBusSpyHookList->invoke(msg); } extern "C" { @@ -531,15 +561,14 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) // a) if it's a local message, we're in the caller's thread, so invoke the filter directly // b) if it's an external message, post to the main thread if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) { - const QDBusSpyHookList &list = *qDBusSpyHookList; if (isLocal) { Q_ASSERT(QThread::currentThread() != thread()); qDBusDebug() << this << "invoking message spies directly"; - QDBusSpyCallEvent::invokeSpyHooks(amsg, list.constData(), list.size()); + QDBusSpyCallEvent::invokeSpyHooks(amsg); } else { qDBusDebug() << this << "invoking message spies via event"; - QCoreApplication::postEvent(qApp, new QDBusSpyCallEvent(this, QDBusConnection(this), - amsg, list.constData(), list.size())); + QCoreApplication::postEvent( + qApp, new QDBusSpyCallEvent(this, QDBusConnection(this), amsg)); // we'll be called back, so return return true; @@ -559,7 +588,7 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack) { - for (auto &node : haystack.children) + for (QDBusConnectionPrivate::ObjectTreeNode &node : haystack.children) huntAndDestroy(needle, node); auto isInactive = [](const QDBusConnectionPrivate::ObjectTreeNode &node) { return !node.isActive(); }; @@ -567,7 +596,7 @@ static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNo if (needle == haystack.obj) { haystack.obj = nullptr; - haystack.flags = 0; + haystack.flags = {}; } } @@ -575,10 +604,10 @@ static void huntAndUnregister(const QList<QStringView> &pathComponents, int i, QDBusConnection::UnregisterMode mode, QDBusConnectionPrivate::ObjectTreeNode *node) { - if (pathComponents.count() == i) { + if (pathComponents.size() == i) { // found it node->obj = nullptr; - node->flags = 0; + node->flags = {}; if (mode == QDBusConnection::UnregisterTree) { // clear the sub-tree as well @@ -603,11 +632,11 @@ static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, bool isScriptable, bool isAdaptor, const QString &path = QString()) { - QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin(); - QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd(); - for ( ; it != end; ++it) { - if (it->isActive()) - huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + u'/' + it->name); + for (const QDBusConnectionPrivate::ObjectTreeNode &node : std::as_const(haystack.children)) { + if (node.isActive()) { + huntAndEmit(connection, msg, needle, node, isScriptable, isAdaptor, + path + u'/' + node.name); + } } if (needle == haystack.obj) { @@ -637,6 +666,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, const QString &signature_, QList<QMetaType> &metaTypes) { QByteArray msgSignature = signature_.toLatin1(); + QString parametersErrorMsg; for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { QMetaMethod mm = mo->method(idx); @@ -663,8 +693,10 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, QString errorMsg; int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg); - if (inputCount == -1) + if (inputCount == -1) { + parametersErrorMsg = errorMsg; continue; // problem parsing + } metaTypes[0] = returnType; bool hasMessage = false; @@ -699,14 +731,14 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, continue; bool ok = true; - for (int j = i; ok && j < metaTypes.count(); ++j) + for (int j = i; ok && j < metaTypes.size(); ++j) if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == nullptr) ok = false; if (!ok) continue; // consistency check: - if (isAsync && metaTypes.count() > i + 1) + if (isAsync && metaTypes.size() > i + 1) continue; if (mm.methodType() == QMetaMethod::Slot) { @@ -726,6 +758,13 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, } // no slot matched + if (!parametersErrorMsg.isEmpty()) { + qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s: %ls", + name.constData(), qUtf16Printable(parametersErrorMsg)); + } else { + qCWarning(dbusIntegration, "QDBusConnection: couldn't handle call to %s, no slot matched", + name.constData()); + } return -1; } @@ -751,13 +790,12 @@ QDBusCallDeliveryEvent *QDBusConnectionPrivate::prepareReply(QDBusConnectionPriv const QDBusMessage &msg) { Q_ASSERT(object); - Q_UNUSED(object); - int n = metaTypes.count() - 1; + int n = metaTypes.size() - 1; if (metaTypes[n] == QDBusMetaTypeId::message()) --n; - if (msg.arguments().count() < n) + if (msg.arguments().size() < n) return nullptr; // too few arguments // check that types match @@ -787,14 +825,15 @@ void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::Signal if (call == DIRECT_DELIVERY) { // short-circuit delivery Q_ASSERT(this == hook.obj); - deliverCall(this, 0, msg, hook.params, hook.midx); + deliverCall(this, msg, hook.params, hook.midx); return; } if (call) postEventToThread(ActivateSignalAction, hook.obj, call); } -bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) +bool QDBusConnectionPrivate::activateCall(QObject *object, QDBusConnection::RegisterOptions flags, + const QDBusMessage &msg) { // This is called by QDBusConnectionPrivate::handleObjectCall to place a call // to a slot on the object. @@ -829,60 +868,59 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBu qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName)); QString cacheKey = msg.member(), signature = msg.signature(); if (!signature.isEmpty()) { - cacheKey.reserve(cacheKey.length() + 1 + signature.length()); + cacheKey.reserve(cacheKey.size() + 1 + signature.size()); cacheKey += u'.'; cacheKey += signature; } - QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey); - while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags && - cacheIt.key() == cacheKey) - ++cacheIt; - if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey) - { + QDBusSlotCache::Key compoundKey{ std::move(cacheKey), flags }; + QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(compoundKey); + if (cacheIt == slotCache.hash.constEnd()) { // not cached, analyze the meta object const QMetaObject *mo = object->metaObject(); QByteArray memberName = msg.member().toUtf8(); // find a slot that matches according to the rules above QDBusSlotCache::Data slotData; - slotData.flags = flags; slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes); if (slotData.slotIdx == -1) { // ### this is where we want to add the connection as an arg too // try with no parameters, but with a QDBusMessage slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes); - if (slotData.metaTypes.count() != 2 || + if (slotData.metaTypes.size() != 2 || slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) { // not found // save the negative lookup slotData.slotIdx = -1; slotData.metaTypes.clear(); - slotCache.hash.insert(cacheKey, slotData); + slotCache.hash.insert(compoundKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); + + qCWarning(dbusIntegration).nospace() << "Could not find slot " << mo->className() + << "::" << memberName.constData(); return false; } } // save to the cache - slotCache.hash.insert(cacheKey, slotData); + slotCache.hash.insert(compoundKey, slotData); object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); // found the slot to be called - deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx); + deliverCall(object, msg, slotData.metaTypes, slotData.slotIdx); return true; } else if (cacheIt->slotIdx == -1) { // negative cache return false; } else { // use the cache - deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx); + deliverCall(object, msg, cacheIt->metaTypes, cacheIt->slotIdx); return true; } return false; } -void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, +void QDBusConnectionPrivate::deliverCall(QObject *object, const QDBusMessage &msg, const QList<QMetaType> &metaTypes, int slotIdx) { Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), @@ -890,10 +928,10 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q "function called for an object that is in another thread!!"); QVarLengthArray<void *, 10> params; - params.reserve(metaTypes.count()); + params.reserve(metaTypes.size()); QVarLengthArray<QVariant, 10> auxParameters; // we cannot allow reallocation here, since we - auxParameters.reserve(metaTypes.count()); // keep references to the entries + auxParameters.reserve(metaTypes.size()); // keep references to the entries // let's create the parameter list @@ -902,13 +940,14 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q // add the input parameters int i; - int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1); + int pCount = qMin(msg.arguments().size(), metaTypes.size() - 1); for (i = 1; i <= pCount; ++i) { auto id = metaTypes[i]; if (id == QDBusMetaTypeId::message()) break; - const QVariant &arg = msg.arguments().at(i - 1); + const QList<QVariant> args = msg.arguments(); + const QVariant &arg = args.at(i - 1); if (arg.metaType() == id) // no conversion needed params.append(const_cast<void *>(arg.constData())); @@ -918,7 +957,7 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q const QDBusArgument &in = *reinterpret_cast<const QDBusArgument *>(arg.constData()); - QVariant &out = auxParameters[auxParameters.count() - 1]; + QVariant &out = auxParameters[auxParameters.size() - 1]; if (Q_UNLIKELY(!QDBusMetaType::demarshall(in, out.metaType(), out.data()))) qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", @@ -933,19 +972,19 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q } } - if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) { + if (metaTypes.size() > i && metaTypes[i] == QDBusMetaTypeId::message()) { params.append(const_cast<void*>(static_cast<const void*>(&msg))); ++i; } // output arguments - const int numMetaTypes = metaTypes.count(); + const int numMetaTypes = metaTypes.size(); QVariantList outputArgs; if (metaTypes[0].id() != QMetaType::Void && metaTypes[0].isValid()) { outputArgs.reserve(numMetaTypes - i + 1); QVariant arg{QMetaType(metaTypes[0])}; outputArgs.append( arg ); - params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); + params[0] = const_cast<void*>(outputArgs.at( outputArgs.size() - 1 ).constData()); } else { outputArgs.reserve(numMetaTypes - i); } @@ -953,7 +992,7 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q for ( ; i < numMetaTypes; ++i) { QVariant arg{QMetaType(metaTypes[i])}; outputArgs.append( arg ); - params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData())); + params.append(const_cast<void*>(outputArgs.at( outputArgs.size() - 1 ).constData())); } // make call: @@ -982,7 +1021,7 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q send(msg.createReply(outputArgs)); } else { // generate internal error - qWarning("Internal error: Failed to deliver message"); + qCWarning(dbusIntegration, "Internal error: Failed to deliver message"); send(msg.createErrorReply(QDBusError::InternalError, "Failed to deliver message"_L1)); } } @@ -990,11 +1029,8 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q return; } -extern bool qDBusInitThreads(); - -QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) - : QObject(p), - ref(1), +QDBusConnectionPrivate::QDBusConnectionPrivate() + : ref(1), mode(InvalidMode), busService(nullptr), connection(nullptr), @@ -1020,12 +1056,8 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) this, &QDBusConnectionPrivate::handleObjectCall, Qt::QueuedConnection); connect(this, &QDBusConnectionPrivate::messageNeedsSending, this, &QDBusConnectionPrivate::sendInternal); - connect(this, &QDBusConnectionPrivate::signalNeedsConnecting, - this, &QDBusConnectionPrivate::addSignalHook, Qt::BlockingQueuedConnection); - connect(this, &QDBusConnectionPrivate::signalNeedsDisconnecting, - this, &QDBusConnectionPrivate::removeSignalHook, Qt::BlockingQueuedConnection); - rootNode.flags = 0; + rootNode.flags = {}; // prepopulate watchedServices: // we know that the owner of org.freedesktop.DBus is itself @@ -1039,9 +1071,10 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) QDBusConnectionPrivate::~QDBusConnectionPrivate() { if (thread() && thread() != QThread::currentThread()) - qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " - "Timer and socket errors will follow and the program will probably crash", - qPrintable(name)); + qCWarning(dbusIntegration, + "QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " + "Timer and socket errors will follow and the program will probably crash", + qPrintable(name)); auto lastMode = mode; // reset on connection close closeConnection(); @@ -1069,12 +1102,8 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate() void QDBusConnectionPrivate::collectAllObjects(QDBusConnectionPrivate::ObjectTreeNode &haystack, QSet<QObject *> &set) { - QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin(); - - while (it != haystack.children.end()) { - collectAllObjects(*it, set); - it++; - } + for (ObjectTreeNode &child : haystack.children) + collectAllObjects(child, set); if (haystack.obj) set.insert(haystack.obj); @@ -1102,11 +1131,9 @@ void QDBusConnectionPrivate::closeConnection() } } - for (auto it = pendingCalls.begin(); it != pendingCalls.end(); ++it) { - auto call = *it; - if (!call->ref.deref()) { + for (QDBusPendingCallPrivate *call : pendingCalls) { + if (!call->ref.deref()) delete call; - } } pendingCalls.clear(); @@ -1117,18 +1144,12 @@ void QDBusConnectionPrivate::closeConnection() // dangling pointer. QSet<QObject *> allObjects; collectAllObjects(rootNode, allObjects); - SignalHookHash::const_iterator sit = signalHooks.constBegin(); - while (sit != signalHooks.constEnd()) { - allObjects.insert(sit.value().obj); - ++sit; - } + for (const SignalHook &signalHook : std::as_const(signalHooks)) + allObjects.insert(signalHook.obj); // now disconnect ourselves - QSet<QObject *>::const_iterator oit = allObjects.constBegin(); - while (oit != allObjects.constEnd()) { - (*oit)->disconnect(this); - ++oit; - } + for (QObject *obj : std::as_const(allObjects)) + obj->disconnect(this); } void QDBusConnectionPrivate::handleDBusDisconnection() @@ -1168,17 +1189,15 @@ void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) void QDBusConnectionPrivate::doDispatch() { if (mode == ClientMode || mode == PeerMode) { - while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; if (dispatchEnabled && !pendingMessages.isEmpty()) { // dispatch previously queued messages - PendingMessageList::Iterator it = pendingMessages.begin(); - PendingMessageList::Iterator end = pendingMessages.end(); - for ( ; it != end; ++it) { - qDBusDebug() << this << "dequeueing message" << *it; - handleMessage(std::move(*it)); + for (QDBusMessage &message : pendingMessages) { + qDBusDebug() << this << "dequeueing message" << message; + handleMessage(std::move(message)); } pendingMessages.clear(); } + while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; } } @@ -1258,8 +1277,8 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error); if (!msg) { - qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(), - qPrintable(error.message())); + qCWarning(dbusIntegration, "QDBusConnection: Could not emit signal %s.%s: %s", + qPrintable(interface), memberName.constData(), qPrintable(error.message())); lastError = error; return; } @@ -1274,46 +1293,46 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name, const QString &oldOwner, const QString &newOwner) { - Q_UNUSED(oldOwner); // QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this); WatchedServicesHash::Iterator it = watchedServices.find(name); if (it == watchedServices.end()) return; if (oldOwner != it->owner) - qWarning("QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'", - qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner)); + qCWarning(dbusIntegration, + "QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'", + qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner)); qDBusDebug() << this << "Updating name" << name << "from" << oldOwner << "to" << newOwner; it->owner = newOwner; } int QDBusConnectionPrivate::findSlot(QObject *obj, const QByteArray &normalizedName, - QList<QMetaType> ¶ms) + QList<QMetaType> ¶ms, QString &errorMsg) { + errorMsg.clear(); int midx = obj->metaObject()->indexOfMethod(normalizedName); if (midx == -1) return -1; - QString errorMsg; int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params, errorMsg); - if ( inputCount == -1 || inputCount + 1 != params.count() ) - return -1; // failed to parse or invalid arguments or output arguments + if (inputCount == -1 || inputCount + 1 != params.size()) + return -1; return midx; } bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, - const QString &service, - const QString &path, const QString &interface, const QString &name, - const ArgMatchRules &argMatch, - QObject *receiver, const char *signal, int minMIdx, - bool buildSignature) + const QString &service, const QString &path, + const QString &interface, const QString &name, + const ArgMatchRules &argMatch, QObject *receiver, + const char *signal, int minMIdx, bool buildSignature, + QString &errorMsg) { QByteArray normalizedName = signal + 1; - hook.midx = findSlot(receiver, signal + 1, hook.params); + hook.midx = findSlot(receiver, signal + 1, hook.params, errorMsg); if (hook.midx == -1) { normalizedName = QMetaObject::normalizedSignature(signal + 1); - hook.midx = findSlot(receiver, normalizedName, hook.params); + hook.midx = findSlot(receiver, normalizedName, hook.params, errorMsg); } if (hook.midx < minMIdx) { return false; @@ -1333,13 +1352,13 @@ bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hoo mname = QString::fromUtf8(normalizedName); } key = mname; - key.reserve(interface.length() + 1 + mname.length()); + key.reserve(interface.size() + 1 + mname.size()); key += u':'; key += interface; if (buildSignature) { hook.signature.clear(); - for (int i = 1; i < hook.params.count(); ++i) + for (int i = 1; i < hook.params.size(); ++i) if (hook.params.at(i) != QDBusMetaTypeId::message()) hook.signature += QLatin1StringView(QDBusMetaType::typeToSignature(hook.params.at(i))); } @@ -1435,7 +1454,7 @@ void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMes } } - if (pathStartPos != msg.path().length()) { + if (pathStartPos != msg.path().size()) { node.flags &= ~QDBusConnection::ExportAllSignals; node.obj = findChildObject(&node, msg.path(), pathStartPos); if (!node.obj) { @@ -1447,19 +1466,16 @@ void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMes QDBusAdaptorConnector *connector; if (node.flags & QDBusConnection::ExportAdaptors && (connector = qDBusFindAdaptorConnector(node.obj))) { - int newflags = node.flags | QDBusConnection::ExportAllSlots; + auto newflags = node.flags | QDBusConnection::ExportAllSlots; if (msg.interface().isEmpty()) { // place the call in all interfaces // let the first one that handles it to work - QDBusAdaptorConnector::AdaptorMap::ConstIterator it = - connector->adaptors.constBegin(); - QDBusAdaptorConnector::AdaptorMap::ConstIterator end = - connector->adaptors.constEnd(); - - for ( ; it != end; ++it) - if (activateCall(it->adaptor, newflags, msg)) + for (const QDBusAdaptorConnector::AdaptorData &adaptorData : + std::as_const(connector->adaptors)) { + if (activateCall(adaptorData.adaptor, newflags, msg)) return; + } } else { // check if we have an interface matching the name that was asked: QDBusAdaptorConnector::AdaptorMap::ConstIterator it; @@ -1653,14 +1669,14 @@ void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) // (but not both) QString key = msg.member(); - key.reserve(key.length() + 1 + msg.interface().length()); + key.reserve(key.size() + 1 + msg.interface().size()); key += u':'; key += msg.interface(); - QDBusReadLocker locker(HandleSignalAction, this); + QDBusWriteLocker locker(HandleSignalAction, this); handleSignal(key, msg); // one try - key.truncate(msg.member().length() + 1); // keep the ':' + key.truncate(msg.member().size() + 1); // keep the ':' handleSignal(key, msg); // second try key = u':'; @@ -1748,10 +1764,10 @@ void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal watchForDBusDisconnection(); - QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection); } -static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnection *connection) +static QDBusConnection::ConnectionCapabilities connectionCapabilities(DBusConnection *connection) { QDBusConnection::ConnectionCapabilities result; typedef dbus_bool_t (*can_send_type_t)(DBusConnection *, int); @@ -1777,7 +1793,7 @@ static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnecti void QDBusConnectionPrivate::handleAuthentication() { - capabilities.storeRelaxed(connectionCapabilies(connection)); + capabilities.storeRelaxed(::connectionCapabilities(connection)); isAuthenticated = true; } @@ -1836,7 +1852,7 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusError qDBusDebug() << this << ": connected successfully"; // schedule a dispatch: - QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection); } extern "C"{ @@ -1844,7 +1860,6 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) { QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data); Q_ASSERT(call->pending == pending); - Q_UNUSED(pending); QDBusConnectionPrivate::processFinishedCall(call); } } @@ -1922,22 +1937,26 @@ bool QDBusConnectionPrivate::send(const QDBusMessage& message) QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error); if (!msg) { if (message.type() == QDBusMessage::MethodCallMessage) - qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", - qPrintable(message.service()), qPrintable(message.path()), - qPrintable(message.interface()), qPrintable(message.member()), - qPrintable(error.message())); + qCWarning(dbusIntegration, + "QDBusConnection: error: could not send message to service \"%s\" path " + "\"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(error.message())); else if (message.type() == QDBusMessage::SignalMessage) - qWarning("QDBusConnection: error: could not send signal to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", - qPrintable(message.service()), - qPrintable(message.path()), qPrintable(message.interface()), - qPrintable(message.member()), - qPrintable(error.message())); + qCWarning(dbusIntegration, + "QDBusConnection: error: could not send signal to service \"%s\" path \"%s\" " + "interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(error.message())); else - qWarning("QDBusConnection: error: could not send %s message to service \"%s\": %s", - message.type() == QDBusMessage::ReplyMessage ? "reply" : - message.type() == QDBusMessage::ErrorMessage ? "error" : - "invalid", qPrintable(message.service()), - qPrintable(error.message())); + qCWarning(dbusIntegration, + "QDBusConnection: error: could not send %s message to service \"%s\": %s", + message.type() == QDBusMessage::ReplyMessage ? "reply" + : message.type() == QDBusMessage::ErrorMessage ? "error" + : "invalid", + qPrintable(message.service()), qPrintable(error.message())); lastError = error; return false; } @@ -1957,7 +1976,7 @@ bool QDBusConnectionPrivate::send(const QDBusMessage& message) class QDBusBlockingCallWatcher { public: - QDBusBlockingCallWatcher(const QDBusMessage &message) + Q_NODISCARD_CTOR QDBusBlockingCallWatcher(const QDBusMessage &message) : m_message(message), m_maxCallTimeoutMs(0) { #if defined(QT_NO_DEBUG) @@ -1984,7 +2003,10 @@ public: if (ok) mainThreadWarningAmount = tmp; else - qWarning("QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS must be an integer; value ignored"); + qCWarning( + dbusIntegration, + "QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS " + "must be an integer; value ignored"); } env = qgetenv("Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS"); @@ -1993,7 +2015,10 @@ public: if (ok) otherThreadWarningAmount = tmp; else - qWarning("QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS must be an integer; value ignored"); + qCWarning(dbusIntegration, + "QDBusBlockingCallWatcher: " + "Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS must be an integer; " + "value ignored"); } initializedAmounts = true; @@ -2018,10 +2043,13 @@ public: return; // disabled if (m_callTimer.elapsed() >= m_maxCallTimeoutMs) { - qWarning("QDBusConnection: warning: blocking call took a long time (%d ms, max for this thread is %d ms) to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"", - int(m_callTimer.elapsed()), m_maxCallTimeoutMs, - qPrintable(m_message.service()), qPrintable(m_message.path()), - qPrintable(m_message.interface()), qPrintable(m_message.member())); + qCWarning( + dbusIntegration, + "QDBusConnection: warning: blocking call took a long time (%d ms, max for this " + "thread is %d ms) to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"", + int(m_callTimer.elapsed()), m_maxCallTimeoutMs, qPrintable(m_message.service()), + qPrintable(m_message.path()), qPrintable(m_message.interface()), + qPrintable(m_message.member())); } } @@ -2031,29 +2059,18 @@ private: QElapsedTimer m_callTimer; }; - QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, - int sendMode, int timeout) + QDBus::CallMode mode, int timeout) { QDBusBlockingCallWatcher watcher(message); QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, nullptr, nullptr, nullptr, timeout); Q_ASSERT(pcall); - if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { - // need to wait for the reply - if (sendMode == QDBus::BlockWithGui) { - pcall->watcherHelper = new QDBusPendingCallWatcherHelper; - QEventLoop loop; - loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::reply, &loop, &QEventLoop::quit); - loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::error, &loop, &QEventLoop::quit); - - // enter the event loop and wait for a reply - loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); - } else { - pcall->waitForFinished(); - } - } + if (mode == QDBus::BlockWithGui) + pcall->waitForFinishedWithGui(); + else + pcall->waitForFinished(); QDBusMessage reply = pcall->replyMessage; lastError = QDBusError(reply); // set or clear error @@ -2083,9 +2100,12 @@ QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &mess // if the message was handled, there might be a reply QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg); if (localReplyMsg.type() == QDBusMessage::InvalidMessage) { - qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') " - "on blocking mode", qPrintable(message.member()), qPrintable(message.path()), - qPrintable(message.signature())); + qCWarning( + dbusIntegration, + "QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') " + "on blocking mode", + qPrintable(message.member()), qPrintable(message.path()), + qPrintable(message.signature())); return QDBusMessage::createError( QDBusError(QDBusError::InternalError, "local-loop message cannot have delayed replies"_L1)); @@ -2111,6 +2131,7 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM pcall->setReplyCallback(receiver, returnMethod); if (errorMethod) { + Q_ASSERT(!pcall->watcherHelper); pcall->watcherHelper = new QDBusPendingCallWatcherHelper; connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod, Qt::QueuedConnection); @@ -2136,10 +2157,12 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error); if (!msg) { - qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", - qPrintable(message.service()), qPrintable(message.path()), - qPrintable(message.interface()), qPrintable(message.member()), - qPrintable(error.message())); + qCWarning(dbusIntegration, + "QDBusConnection: error: could not send message to service \"%s\" path \"%s\" " + "interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(error.message())); pcall->replyMessage = QDBusMessage::createError(error); lastError = error; processFinishedCall(pcall); @@ -2210,15 +2233,30 @@ bool QDBusConnectionPrivate::connectSignal(const QString &service, QString key; hook.signature = signature; - if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) + QString errorMsg; + if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, + false, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not connect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg); return false; // don't connect + } Q_ASSERT(thread() != QThread::currentThread()); - return emit signalNeedsConnecting(key, hook); + return addSignalHook(key, hook); } bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook &hook) { + bool result = false; + + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::addSignalHookImpl, + Qt::BlockingQueuedConnection, qReturnArg(result), key, hook); + + return result; +} + +bool QDBusConnectionPrivate::addSignalHookImpl(const QString &key, const SignalHook &hook) +{ QDBusWriteLocker locker(ConnectAction, this); // avoid duplicating: @@ -2300,15 +2338,30 @@ bool QDBusConnectionPrivate::disconnectSignal(const QString &service, name2.detach(); hook.signature = signature; - if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) + QString errorMsg; + if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, + false, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not disconnect" << interface << "to" << slot + 1 << ":" << qPrintable(errorMsg); return false; // don't disconnect + } Q_ASSERT(thread() != QThread::currentThread()); - return emit signalNeedsDisconnecting(key, hook); + return removeSignalHook(key, hook); } bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHook &hook) { + bool result = false; + + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::removeSignalHookImpl, + Qt::BlockingQueuedConnection, qReturnArg(result), key, hook); + + return result; +} + +bool QDBusConnectionPrivate::removeSignalHookImpl(const QString &key, const SignalHook &hook) +{ // remove it from our list: QDBusWriteLocker locker(ConnectAction, this); QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key); @@ -2339,7 +2392,9 @@ QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it) bool erase = false; MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule); if (i == matchRefCounts.end()) { - qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!"); + qCWarning(dbusIntegration, + "QDBusConnectionPrivate::disconnectSignal: MatchRule not found in " + "matchRefCounts!!"); } else { if (i.value() == 1) { erase = true; @@ -2393,8 +2448,8 @@ void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) connector->connectAllSignals(node->obj); } - connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), - this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + connect(connector, &QDBusAdaptorConnector::relaySignal, this, + &QDBusConnectionPrivate::relaySignal, Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); } } @@ -2427,12 +2482,16 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, - QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) { + qCWarning(dbusIntegration) + << "Could not connect" << interface << "to" << signal.name() << ":" << qPrintable(errorMsg); return; // don't connect + } Q_ASSERT(thread() != QThread::currentThread()); - emit signalNeedsConnecting(key, hook); + addSignalHook(key, hook); } void QDBusConnectionPrivate::disconnectRelay(const QString &service, @@ -2448,12 +2507,16 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); + QString errorMsg; if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, - QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + QDBusAbstractInterface::staticMetaObject.methodCount(), true, errorMsg)) { + qCWarning(dbusIntegration) << "Could not disconnect" << interface << "to" + << signal.methodSignature() << ":" << qPrintable(errorMsg); return; // don't disconnect + } Q_ASSERT(thread() != QThread::currentThread()); - emit signalNeedsDisconnecting(key, hook); + removeSignalHook(key, hook); } bool QDBusConnectionPrivate::shouldWatchService(const QString &service) @@ -2638,6 +2701,28 @@ void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEve QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this); } +/* + * Enable dispatch of D-Bus events for this connection, but only after + * context's thread's event loop has started and processed any already + * pending events. The event dispatch is then enabled in the DBus aux thread. + */ +void QDBusConnectionPrivate::enableDispatchDelayed(QObject *context) +{ + ref.ref(); + QMetaObject::invokeMethod( + context, + [this]() { + // This call cannot race with something disabling dispatch only + // because dispatch is never re-disabled from Qt code on an + // in-use connection once it has been enabled. + QMetaObject::invokeMethod(this, &QDBusConnectionPrivate::setDispatchEnabled, + Qt::QueuedConnection, true); + if (!ref.deref()) + deleteLater(); + }, + Qt::QueuedConnection); +} + QT_END_NAMESPACE #endif // QT_NO_DBUS |