diff options
Diffstat (limited to 'src/dbus')
-rw-r--r-- | src/dbus/dbus_minimal_p.h | 2 | ||||
-rw-r--r-- | src/dbus/qdbus_symbols.cpp | 5 | ||||
-rw-r--r-- | src/dbus/qdbusargument_p.h | 2 | ||||
-rw-r--r-- | src/dbus/qdbusconnection.cpp | 46 | ||||
-rw-r--r-- | src/dbus/qdbusconnection_p.h | 3 | ||||
-rw-r--r-- | src/dbus/qdbusintegrator.cpp | 47 | ||||
-rw-r--r-- | src/dbus/qdbusmisc.cpp | 20 | ||||
-rw-r--r-- | src/dbus/qdbusutil.cpp | 58 | ||||
-rw-r--r-- | src/dbus/qdbusutil_p.h | 11 |
9 files changed, 146 insertions, 48 deletions
diff --git a/src/dbus/dbus_minimal_p.h b/src/dbus/dbus_minimal_p.h index 8b40742e0c..b52b0153e4 100644 --- a/src/dbus/dbus_minimal_p.h +++ b/src/dbus/dbus_minimal_p.h @@ -105,9 +105,11 @@ typedef dbus_uint32_t dbus_bool_t; /* dbus-shared.h */ #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" #define DBUS_PATH_DBUS "/org/freedesktop/DBus" +#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local" #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" #define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" #define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" +#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local" #define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */ #define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */ diff --git a/src/dbus/qdbus_symbols.cpp b/src/dbus/qdbus_symbols.cpp index b16be6637e..f91a8d2176 100644 --- a/src/dbus/qdbus_symbols.cpp +++ b/src/dbus/qdbus_symbols.cpp @@ -43,7 +43,6 @@ #include <QtCore/qlibrary.h> #endif #include <QtCore/qmutex.h> -#include <private/qmutexpool_p.h> #ifndef QT_NO_DBUS @@ -81,8 +80,10 @@ bool qdbus_loadLibDBus() static bool triedToLoadLibrary = false; #ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&qdbus_resolve_me)); + static QBasicMutex mutex; + QMutexLocker locker(&mutex); #endif + QLibrary *&lib = qdbus_libdbus; if (triedToLoadLibrary) return lib && lib->isLoaded(); diff --git a/src/dbus/qdbusargument_p.h b/src/dbus/qdbusargument_p.h index 296918961f..ca0a2cde13 100644 --- a/src/dbus/qdbusargument_p.h +++ b/src/dbus/qdbusargument_p.h @@ -72,7 +72,7 @@ public: inline QDBusArgumentPrivate(int flags = 0) : message(0), ref(1), capabilities(flags) { } - ~QDBusArgumentPrivate(); + virtual ~QDBusArgumentPrivate(); static bool checkRead(QDBusArgumentPrivate *d); static bool checkReadAndDetach(QDBusArgumentPrivate *&d); diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index ea8c2b0311..bd25d8a6bf 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -44,6 +44,7 @@ #include <qdebug.h> #include <qcoreapplication.h> #include <qstringlist.h> +#include <qvector.h> #include <qtimer.h> #include <qthread.h> @@ -68,6 +69,10 @@ QT_BEGIN_NAMESPACE +#ifdef Q_OS_WIN +static void preventDllUnload(); +#endif + Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager) // can be replaced with a lambda in Qt 5.7 @@ -156,6 +161,10 @@ QDBusConnectionManager::QDBusConnectionManager() this, &QDBusConnectionManager::createServer, Qt::BlockingQueuedConnection); moveToThread(this); // ugly, don't do this in other projects +#ifdef Q_OS_WIN + // prevent the library from being unloaded on Windows. See comments in the function. + preventDllUnload(); +#endif defaultBuses[0] = defaultBuses[1] = Q_NULLPTR; start(); } @@ -909,8 +918,8 @@ bool QDBusConnection::registerObject(const QString &path, const QString &interfa if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) return false; - QStringList pathComponents = path.split(QLatin1Char('/')); - if (pathComponents.last().isEmpty()) + auto pathComponents = path.splitRef(QLatin1Char('/')); + if (pathComponents.constLast().isEmpty()) pathComponents.removeLast(); QDBusWriteLocker locker(RegisterObjectAction, d); @@ -965,7 +974,7 @@ bool QDBusConnection::registerObject(const QString &path, const QString &interfa } } else { // add entry - node = node->children.insert(it, pathComponents.at(i)); + node = node->children.insert(it, pathComponents.at(i).toString()); } // iterate @@ -1017,8 +1026,8 @@ QObject *QDBusConnection::objectRegisteredAt(const QString &path) const if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) return 0; - QStringList pathComponents = path.split(QLatin1Char('/')); - if (pathComponents.last().isEmpty()) + auto pathComponents = path.splitRef(QLatin1Char('/')); + if (pathComponents.constLast().isEmpty()) pathComponents.removeLast(); // lower-bound search for where this object should enter in the tree @@ -1281,4 +1290,31 @@ QT_END_NAMESPACE #include "qdbusconnection.moc" +#ifdef Q_OS_WIN +# include <qt_windows.h> + +QT_BEGIN_NAMESPACE +static void preventDllUnload() +{ + // Thread termination is really wacky on Windows. For some reason we don't + // understand, exiting from the thread may try to unload the DLL. Since the + // QDBusConnectionManager thread runs until the DLL is unloaded, we've got + // a deadlock: the main thread is waiting for the manager thread to exit, + // but the manager thread is attempting to acquire a lock to unload the DLL. + // + // We work around the issue by preventing the unload from happening in the + // first place. + // + // For this trick, see + // https://blogs.msdn.microsoft.com/oldnewthing/20131105-00/?p=2733 + + static HMODULE self; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_PIN, + reinterpret_cast<const wchar_t *>(&self), // any address in this DLL + &self); +} +QT_END_NAMESPACE +#endif + #endif // QT_NO_DBUS diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h index b2fa8faae8..f00988c05f 100644 --- a/src/dbus/qdbusconnection_p.h +++ b/src/dbus/qdbusconnection_p.h @@ -265,6 +265,8 @@ private: QString getNameOwnerNoCache(const QString &service); + void watchForDBusDisconnection(); + void _q_newConnection(QDBusConnectionPrivate *newConnection); protected: @@ -284,6 +286,7 @@ private slots: void serviceOwnerChangedNoLock(const QString &name, const QString &oldOwner, const QString &newOwner); void registerServiceNoLock(const QString &serviceName); void unregisterServiceNoLock(const QString &serviceName); + void handleDBusDisconnection(); signals: void dispatchStatusChanged(); diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 6d4a27cdb5..c73f808485 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -586,7 +586,7 @@ static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNo } } -static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusConnection::UnregisterMode mode, +static void huntAndUnregister(const QVector<QStringRef> &pathComponents, int i, QDBusConnection::UnregisterMode mode, QDBusConnectionPrivate::ObjectTreeNode *node) { if (pathComponents.count() == i) { @@ -1126,6 +1126,12 @@ void QDBusConnectionPrivate::closeConnection() rootNode.children.clear(); // free resources } +void QDBusConnectionPrivate::handleDBusDisconnection() +{ + while (!pendingCalls.isEmpty()) + processFinishedCall(pendingCalls.first()); +} + void QDBusConnectionPrivate::checkThread() { Q_ASSERT(thread() == QDBusConnectionManager::instance()); @@ -1651,6 +1657,19 @@ void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) handleSignal(key, msg); // third try } +void QDBusConnectionPrivate::watchForDBusDisconnection() +{ + SignalHook hook; + // Initialize the hook for Disconnected signal + hook.service.clear(); // org.freedesktop.DBus.Local.Disconnected uses empty service name + hook.path = QDBusUtil::dbusPathLocal(); + hook.obj = this; + hook.params << QMetaType::Void; + hook.midx = staticMetaObject.indexOfSlot("handleDBusDisconnection()"); + Q_ASSERT(hook.midx != -1); + signalHooks.insert(QLatin1String("Disconnected:" DBUS_INTERFACE_LOCAL), hook); +} + void QDBusConnectionPrivate::setServer(QDBusServer *object, DBusServer *s, const QDBusErrorInternal &error) { mode = ServerMode; @@ -1716,6 +1735,8 @@ void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal qDBusSignalFilter, this, 0); + watchForDBusDisconnection(); + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); } @@ -1792,6 +1813,8 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusError Q_ASSERT(hook.midx != -1); signalHooks.insert(QLatin1String("NameOwnerChanged:" DBUS_INTERFACE_DBUS), hook); + watchForDBusDisconnection(); + qDBusDebug() << this << ": connected successfully"; // schedule a dispatch: @@ -1818,10 +1841,16 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) QDBusMessage &msg = call->replyMessage; if (call->pending) { - // decode the message - DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending); - msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities); - q_dbus_message_unref(reply); + // when processFinishedCall is called and pending call is not completed, + // it means we received disconnected signal from libdbus + if (q_dbus_pending_call_get_completed(call->pending)) { + // decode the message + DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending); + msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities); + q_dbus_message_unref(reply); + } else { + msg = QDBusMessage::createError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage()); + } } qDBusDebug() << connection << "got message reply:" << msg; @@ -2121,8 +2150,8 @@ void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void * pcall->pending = pending; q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0); - // DBus won't notify us when a peer disconnects so we need to track these ourselves - if (mode == QDBusConnectionPrivate::PeerMode) + // DBus won't notify us when a peer disconnects or server terminates so we need to track these ourselves + if (mode == QDBusConnectionPrivate::PeerMode || mode == QDBusConnectionPrivate::ClientMode) pendingCalls.append(pcall); return; @@ -2335,12 +2364,12 @@ void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) void QDBusConnectionPrivate::unregisterObject(const QString &path, QDBusConnection::UnregisterMode mode) { QDBusConnectionPrivate::ObjectTreeNode *node = &rootNode; - QStringList pathComponents; + QVector<QStringRef> pathComponents; int i; if (path == QLatin1String("/")) { i = 0; } else { - pathComponents = path.split(QLatin1Char('/')); + pathComponents = path.splitRef(QLatin1Char('/')); i = 1; } diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp index 6ecd66f3e7..930c3bd2da 100644 --- a/src/dbus/qdbusmisc.cpp +++ b/src/dbus/qdbusmisc.cpp @@ -94,14 +94,20 @@ QString qDBusInterfaceFromMetaObject(const QMetaObject *mo) interface.prepend(QLatin1String("local.")); } else { interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName()); - QStringList domainName = - QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'), - QString::SkipEmptyParts); - if (domainName.isEmpty()) + const QString organizationDomain = QCoreApplication::instance()->organizationDomain(); + const auto domainName = organizationDomain.splitRef(QLatin1Char('.'), QString::SkipEmptyParts); + if (domainName.isEmpty()) { interface.prepend(QLatin1String("local.")); - else - for (int i = 0; i < domainName.count(); ++i) - interface.prepend(QLatin1Char('.')).prepend(domainName.at(i)); + } else { + QString composedDomain; + // + 1 for additional dot, e.g. organizationDomain equals "example.com", + // then composedDomain will be equal "com.example." + composedDomain.reserve(organizationDomain.size() + 1); + for (auto it = domainName.rbegin(), end = domainName.rend(); it != end; ++it) + composedDomain += *it + QLatin1Char('.'); + + interface.prepend(composedDomain); + } } } diff --git a/src/dbus/qdbusutil.cpp b/src/dbus/qdbusutil.cpp index aababd12e9..28341a71a8 100644 --- a/src/dbus/qdbusutil.cpp +++ b/src/dbus/qdbusutil.cpp @@ -42,6 +42,7 @@ #include "qdbus_symbols_p.h" #include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> #include "qdbusargument.h" #include "qdbusunixfiledescriptor.h" @@ -330,10 +331,10 @@ namespace QDBusUtil /*! \internal - \fn bool QDBusUtil::isValidPartOfObjectPath(const QString &part) + \fn bool QDBusUtil::isValidPartOfObjectPath(const QStringRef &part) See QDBusUtil::isValidObjectPath */ - bool isValidPartOfObjectPath(const QString &part) + bool isValidPartOfObjectPath(const QStringRef &part) { if (part.isEmpty()) return false; // can't be valid if it's empty @@ -347,6 +348,13 @@ namespace QDBusUtil } /*! + \internal + \fn bool QDBusUtil::isValidPartOfObjectPath(const QString &part) + + \overload + */ + + /*! \fn bool QDBusUtil::isValidInterfaceName(const QString &ifaceName) Returns \c true if this is \a ifaceName is a valid interface name. @@ -364,36 +372,35 @@ namespace QDBusUtil if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH) return false; - QStringList parts = ifaceName.split(QLatin1Char('.')); + const auto parts = ifaceName.splitRef(QLatin1Char('.')); if (parts.count() < 2) return false; // at least two parts - for (int i = 0; i < parts.count(); ++i) - if (!isValidMemberName(parts.at(i))) + for (const QStringRef &part : parts) + if (!isValidMemberName(part)) return false; return true; } /*! - \fn bool QDBusUtil::isValidUniqueConnectionName(const QString &connName) + \fn bool QDBusUtil::isValidUniqueConnectionName(const QStringRef &connName) Returns \c true if \a connName is a valid unique connection name. Unique connection names start with a colon (":") and are followed by a list of dot-separated components composed of ASCII letters, digits, the hyphen or the underscore ("_") character. */ - bool isValidUniqueConnectionName(const QString &connName) + bool isValidUniqueConnectionName(const QStringRef &connName) { if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH || !connName.startsWith(QLatin1Char(':'))) return false; - QStringList parts = connName.mid(1).split(QLatin1Char('.')); + const auto parts = connName.mid(1).split(QLatin1Char('.')); if (parts.count() < 1) return false; - for (int i = 0; i < parts.count(); ++i) { - const QString &part = parts.at(i); + for (const QStringRef &part : parts) { if (part.isEmpty()) return false; @@ -407,6 +414,12 @@ namespace QDBusUtil } /*! + \fn bool QDBusUtil::isValidUniqueConnectionName(const QString &connName) + + \overload + */ + + /*! \fn bool QDBusUtil::isValidBusName(const QString &busName) Returns \c true if \a busName is a valid bus name. @@ -429,12 +442,11 @@ namespace QDBusUtil if (busName.startsWith(QLatin1Char(':'))) return isValidUniqueConnectionName(busName); - QStringList parts = busName.split(QLatin1Char('.')); + const auto parts = busName.splitRef(QLatin1Char('.')); if (parts.count() < 1) return false; - for (int i = 0; i < parts.count(); ++i) { - const QString &part = parts.at(i); + for (const QStringRef &part : parts) { if (part.isEmpty()) return false; @@ -450,12 +462,12 @@ namespace QDBusUtil } /*! - \fn bool QDBusUtil::isValidMemberName(const QString &memberName) + \fn bool QDBusUtil::isValidMemberName(const QStringRef &memberName) Returns \c true if \a memberName is a valid member name. A valid member name does not exceed 255 characters in length, is not empty, is composed only of ASCII letters, digits and underscores, but does not start with a digit. */ - bool isValidMemberName(const QString &memberName) + bool isValidMemberName(const QStringRef &memberName) { if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH) return false; @@ -470,6 +482,12 @@ namespace QDBusUtil } /*! + \fn bool QDBusUtil::isValidMemberName(const QString &memberName) + + \overload + */ + + /*! \fn bool QDBusUtil::isValidErrorName(const QString &errorName) Returns \c true if \a errorName is a valid error name. Valid error names are valid interface names and vice-versa, so this function is actually an alias for isValidInterfaceName. @@ -501,12 +519,10 @@ namespace QDBusUtil path.endsWith(QLatin1Char('/'))) return false; - QStringList parts = path.split(QLatin1Char('/')); - Q_ASSERT(parts.count() >= 1); - parts.removeFirst(); // it starts with /, so we get an empty first part - - for (int i = 0; i < parts.count(); ++i) - if (!isValidPartOfObjectPath(parts.at(i))) + // it starts with /, so we skip the empty first part + const auto parts = path.midRef(1).split(QLatin1Char('/')); + for (const QStringRef &part : parts) + if (!isValidPartOfObjectPath(part)) return false; return true; diff --git a/src/dbus/qdbusutil_p.h b/src/dbus/qdbusutil_p.h index 27b61ad7f2..e11fe573b5 100644 --- a/src/dbus/qdbusutil_p.h +++ b/src/dbus/qdbusutil_p.h @@ -68,15 +68,18 @@ namespace QDBusUtil { Q_DBUS_EXPORT bool isValidInterfaceName(const QString &ifaceName); - Q_DBUS_EXPORT bool isValidUniqueConnectionName(const QString &busName); + Q_DBUS_EXPORT bool isValidUniqueConnectionName(const QStringRef &busName); + bool inline isValidUniqueConnectionName(const QString &busName) { return isValidUniqueConnectionName(QStringRef(&busName)); } Q_DBUS_EXPORT bool isValidBusName(const QString &busName); - Q_DBUS_EXPORT bool isValidMemberName(const QString &memberName); + Q_DBUS_EXPORT bool isValidMemberName(const QStringRef &memberName); + bool inline isValidMemberName(const QString &memberName) { return isValidMemberName(QStringRef(&memberName)); } Q_DBUS_EXPORT bool isValidErrorName(const QString &errorName); - Q_DBUS_EXPORT bool isValidPartOfObjectPath(const QString &path); + Q_DBUS_EXPORT bool isValidPartOfObjectPath(const QStringRef &path); + bool inline isValidPartOfObjectPath(const QString &path) { return isValidPartOfObjectPath(QStringRef(&path)); } Q_DBUS_EXPORT bool isValidObjectPath(const QString &path); @@ -161,6 +164,8 @@ namespace QDBusUtil { return QStringLiteral(DBUS_SERVICE_DBUS); } inline QString dbusPath() { return QStringLiteral(DBUS_PATH_DBUS); } + inline QString dbusPathLocal() + { return QStringLiteral(DBUS_PATH_LOCAL); } inline QString dbusInterface() { // it's the same string, but just be sure |