summaryrefslogtreecommitdiffstats
path: root/src/dbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbus')
-rw-r--r--src/dbus/dbus_minimal_p.h2
-rw-r--r--src/dbus/qdbus_symbols.cpp5
-rw-r--r--src/dbus/qdbusargument_p.h2
-rw-r--r--src/dbus/qdbusconnection.cpp46
-rw-r--r--src/dbus/qdbusconnection_p.h3
-rw-r--r--src/dbus/qdbusintegrator.cpp47
-rw-r--r--src/dbus/qdbusmisc.cpp20
-rw-r--r--src/dbus/qdbusutil.cpp58
-rw-r--r--src/dbus/qdbusutil_p.h11
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