diff options
Diffstat (limited to 'src/dbus/qdbusconnection.cpp')
-rw-r--r-- | src/dbus/qdbusconnection.cpp | 371 |
1 files changed, 52 insertions, 319 deletions
diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index 9438641147..f6918b70b0 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -1,62 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtDBus module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdbusconnection.h" #include "qdbusconnection_p.h" #include <qdebug.h> -#include <qcoreapplication.h> #include <qstringlist.h> -#include <qtimer.h> -#include <qthread.h> -#include <QtCore/private/qlocking_p.h> #include "qdbusconnectioninterface.h" #include "qdbuserror.h" #include "qdbusmessage.h" -#include "qdbusmessage_p.h" -#include "qdbusinterface_p.h" #include "qdbusutil_p.h" #include "qdbusconnectionmanager_p.h" #include "qdbuspendingcall_p.h" - #include "qdbusthreaddebug_p.h" #include <algorithm> @@ -69,219 +26,6 @@ QT_BEGIN_NAMESPACE -#ifdef Q_OS_WIN -static void preventDllUnload(); -#endif - -Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager) - -QDBusConnectionPrivate *QDBusConnectionManager::busConnection(QDBusConnection::BusType type) -{ - static_assert(int(QDBusConnection::SessionBus) + int(QDBusConnection::SystemBus) == 1); - Q_ASSERT(type == QDBusConnection::SessionBus || type == QDBusConnection::SystemBus); - - if (!qdbus_loadLibDBus()) - return nullptr; - - // we'll start in suspended delivery mode if we're in the main thread - // (the event loop will resume delivery) - bool suspendedDelivery = qApp && qApp->thread() == QThread::currentThread(); - - const auto locker = qt_scoped_lock(defaultBusMutex); - if (defaultBuses[type]) - return defaultBuses[type]; - - QString name = QStringLiteral("qt_default_session_bus"); - if (type == QDBusConnection::SystemBus) - name = QStringLiteral("qt_default_system_bus"); - return defaultBuses[type] = connectToBus(type, name, suspendedDelivery); -} - -QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const -{ - return connectionHash.value(name, nullptr); -} - -void QDBusConnectionManager::removeConnection(const QString &name) -{ - QDBusConnectionPrivate *d = nullptr; - d = connectionHash.take(name); - if (d && !d->ref.deref()) - d->deleteLater(); - - // Static objects may be keeping the connection open. - // However, it is harmless to have outstanding references to a connection that is - // closing as long as those references will be soon dropped without being used. - - // ### Output a warning if connections are being used after they have been removed. -} - -QDBusConnectionManager::QDBusConnectionManager() -{ - connect(this, &QDBusConnectionManager::connectionRequested, - this, &QDBusConnectionManager::executeConnectionRequest, Qt::BlockingQueuedConnection); - connect(this, &QDBusConnectionManager::serverRequested, - 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] = nullptr; - start(); -} - -QDBusConnectionManager::~QDBusConnectionManager() -{ - quit(); - wait(); -} - -QDBusConnectionManager* QDBusConnectionManager::instance() -{ - return _q_manager(); -} - -Q_DBUS_EXPORT void qDBusBindToApplication(); -void qDBusBindToApplication() -{ -} - -void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c) -{ - connectionHash[name] = c; - c->name = name; -} - -void QDBusConnectionManager::run() -{ - exec(); - - // cleanup: - const auto locker = qt_scoped_lock(mutex); - for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin(); - it != connectionHash.constEnd(); ++it) { - QDBusConnectionPrivate *d = it.value(); - if (!d->ref.deref()) { - delete d; - } else { - d->closeConnection(); - d->moveToThread(nullptr); // allow it to be deleted in another thread - } - } - connectionHash.clear(); - - // allow deletion from any thread without warning - moveToThread(nullptr); -} - -QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(QDBusConnection::BusType type, const QString &name, - bool suspendedDelivery) -{ - ConnectionRequestData data; - data.type = ConnectionRequestData::ConnectToStandardBus; - data.busType = type; - data.name = &name; - data.suspendedDelivery = suspendedDelivery; - - emit connectionRequested(&data); - if (suspendedDelivery && data.result->connection) { - data.result->ref.ref(); - QDBusConnectionDispatchEnabler *o = new QDBusConnectionDispatchEnabler(data.result); - QTimer::singleShot(0, o, SLOT(execute())); - o->moveToThread(qApp->thread()); // qApp was checked in the caller - } - return data.result; -} - -QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(const QString &address, const QString &name) -{ - ConnectionRequestData data; - data.type = ConnectionRequestData::ConnectToBusByAddress; - data.busAddress = &address; - data.name = &name; - data.suspendedDelivery = false; - - emit connectionRequested(&data); - return data.result; -} - -QDBusConnectionPrivate *QDBusConnectionManager::connectToPeer(const QString &address, const QString &name) -{ - ConnectionRequestData data; - data.type = ConnectionRequestData::ConnectToPeerByAddress; - data.busAddress = &address; - data.name = &name; - data.suspendedDelivery = false; - - emit connectionRequested(&data); - return data.result; -} - -void QDBusConnectionManager::executeConnectionRequest(QDBusConnectionManager::ConnectionRequestData *data) -{ - const auto locker = qt_scoped_lock(mutex); - const QString &name = *data->name; - QDBusConnectionPrivate *&d = data->result; - - // check if the connection exists by name - d = connection(name); - if (d || name.isEmpty()) - return; - - d = new QDBusConnectionPrivate; - DBusConnection *c = nullptr; - QDBusErrorInternal error; - switch (data->type) { - case ConnectionRequestData::ConnectToStandardBus: - switch (data->busType) { - case QDBusConnection::SystemBus: - c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error); - break; - case QDBusConnection::SessionBus: - c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error); - break; - case QDBusConnection::ActivationBus: - c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error); - break; - } - break; - - case ConnectionRequestData::ConnectToBusByAddress: - case ConnectionRequestData::ConnectToPeerByAddress: - c = q_dbus_connection_open_private(data->busAddress->toUtf8().constData(), error); - if (c && data->type == ConnectionRequestData::ConnectToBusByAddress) { - // register on the bus - if (!q_dbus_bus_register(c, error)) { - q_dbus_connection_unref(c); - c = nullptr; - } - } - break; - } - - setConnection(name, d); - if (data->type == ConnectionRequestData::ConnectToPeerByAddress) { - d->setPeer(c, error); - } else { - // create the bus service - // will lock in QDBusConnectionPrivate::connectRelay() - d->setConnection(c, error); - d->createBusService(); - if (c && data->suspendedDelivery) - d->setDispatchEnabled(false); - } -} - -void QDBusConnectionManager::createServer(const QString &address, void *server) -{ - QDBusErrorInternal error; - QDBusConnectionPrivate *d = new QDBusConnectionPrivate; - d->setServer(static_cast<QDBusServer *>(server), - q_dbus_server_listen(address.toUtf8().constData(), error), error); -} - /*! \class QDBusConnection \inmodule QtDBus @@ -406,13 +150,17 @@ void QDBusConnectionManager::createServer(const QString &address, void *server) */ QDBusConnection::QDBusConnection(const QString &name) { - if (name.isEmpty() || _q_manager.isDestroyed()) { + if (name.isEmpty()) { + d = nullptr; + return; + } + + auto *manager = QDBusConnectionManager::instance(); + + if (!manager) { d = nullptr; } else { - const auto locker = qt_scoped_lock(_q_manager()->mutex); - d = _q_manager()->connection(name); - if (d) - d->ref.ref(); + d = manager->existingConnection(name); } } @@ -465,17 +213,19 @@ QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other) } /*! - Opens a connection of type \a type to one of the known busses and + Opens a connection of type \a type to one of the known buses and associate with it the connection name \a name. Returns a QDBusConnection object associated with that connection. */ QDBusConnection QDBusConnection::connectToBus(BusType type, const QString &name) { - if (_q_manager.isDestroyed() || !qdbus_loadLibDBus()) { + auto *manager = QDBusConnectionManager::instance(); + + if (!manager || !qdbus_loadLibDBus()) { QDBusConnectionPrivate *d = nullptr; return QDBusConnection(d); } - return QDBusConnection(_q_manager()->connectToBus(type, name, false)); + return QDBusConnection(manager->connectToBus(type, name, false)); } /*! @@ -485,11 +235,13 @@ QDBusConnection QDBusConnection::connectToBus(BusType type, const QString &name) QDBusConnection QDBusConnection::connectToBus(const QString &address, const QString &name) { - if (_q_manager.isDestroyed() || !qdbus_loadLibDBus()) { + auto *manager = QDBusConnectionManager::instance(); + + if (!manager || !qdbus_loadLibDBus()) { QDBusConnectionPrivate *d = nullptr; return QDBusConnection(d); } - return QDBusConnection(_q_manager()->connectToBus(address, name)); + return QDBusConnection(manager->connectToBus(address, name)); } /*! \since 4.8 @@ -500,11 +252,13 @@ QDBusConnection QDBusConnection::connectToBus(const QString &address, QDBusConnection QDBusConnection::connectToPeer(const QString &address, const QString &name) { - if (_q_manager.isDestroyed() || !qdbus_loadLibDBus()) { + auto *manager = QDBusConnectionManager::instance(); + + if (!manager || !qdbus_loadLibDBus()) { QDBusConnectionPrivate *d = nullptr; return QDBusConnection(d); } - return QDBusConnection(_q_manager()->connectToPeer(address, name)); + return QDBusConnection(manager->connectToPeer(address, name)); } /*! @@ -517,13 +271,11 @@ QDBusConnection QDBusConnection::connectToPeer(const QString &address, */ void QDBusConnection::disconnectFromBus(const QString &name) { - if (_q_manager()) { - const auto locker = qt_scoped_lock(_q_manager()->mutex); - QDBusConnectionPrivate *d = _q_manager()->connection(name); - if (d && d->mode != QDBusConnectionPrivate::ClientMode) - return; - _q_manager()->removeConnection(name); - } + auto *manager = QDBusConnectionManager::instance(); + if (!manager) + return; + + manager->disconnectFrom(name, QDBusConnectionPrivate::ClientMode); } /*! @@ -538,13 +290,11 @@ void QDBusConnection::disconnectFromBus(const QString &name) */ void QDBusConnection::disconnectFromPeer(const QString &name) { - if (_q_manager()) { - const auto locker = qt_scoped_lock(_q_manager()->mutex); - QDBusConnectionPrivate *d = _q_manager()->connection(name); - if (d && d->mode != QDBusConnectionPrivate::PeerMode) - return; - _q_manager()->removeConnection(name); - } + auto *manager = QDBusConnectionManager::instance(); + if (!manager) + return; + + manager->disconnectFrom(name, QDBusConnectionPrivate::PeerMode); } /*! @@ -681,6 +431,9 @@ QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode See the QDBusInterface::asyncCall() function for a more friendly way of placing calls. + + \note Method calls to objects registered by the application itself are never + asynchronous due to implementation limitations. */ QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int timeout) const { @@ -889,7 +642,7 @@ bool QDBusConnection::registerObject(const QString &path, const QString &interfa if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) return false; - auto pathComponents = QStringView{path}.split(QLatin1Char('/')); + auto pathComponents = QStringView{path}.split(u'/'); if (pathComponents.constLast().isEmpty()) pathComponents.removeLast(); QDBusWriteLocker locker(RegisterObjectAction, d); @@ -898,7 +651,7 @@ bool QDBusConnection::registerObject(const QString &path, const QString &interfa QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; int i = 1; while (node) { - if (pathComponents.count() == i) { + if (pathComponents.size() == i) { // this node exists // consider it free if there's no object here and the user is not trying to // replace the object sub-tree @@ -998,7 +751,7 @@ QObject *QDBusConnection::objectRegisteredAt(const QString &path) const if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) return nullptr; - auto pathComponents = QStringView{path}.split(QLatin1Char('/')); + auto pathComponents = QStringView{path}.split(u'/'); if (pathComponents.constLast().isEmpty()) pathComponents.removeLast(); @@ -1008,7 +761,7 @@ QObject *QDBusConnection::objectRegisteredAt(const QString &path) const int i = 1; while (node) { - if (pathComponents.count() == i) + if (pathComponents.size() == i) return node->obj; if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) return node->obj; @@ -1164,9 +917,11 @@ bool QDBusConnection::unregisterService(const QString &serviceName) */ QDBusConnection QDBusConnection::sessionBus() { - if (_q_manager.isDestroyed()) + auto *manager = QDBusConnectionManager::instance(); + + if (!manager) return QDBusConnection(nullptr); - return QDBusConnection(_q_manager()->busConnection(SessionBus)); + return QDBusConnection(manager->busConnection(SessionBus)); } /*! @@ -1178,9 +933,11 @@ QDBusConnection QDBusConnection::sessionBus() */ QDBusConnection QDBusConnection::systemBus() { - if (_q_manager.isDestroyed()) + auto *manager = QDBusConnectionManager::instance(); + + if (!manager) return QDBusConnection(nullptr); - return QDBusConnection(_q_manager()->busConnection(SystemBus)); + return QDBusConnection(manager->busConnection(SystemBus)); } /*! @@ -1255,31 +1012,7 @@ QByteArray QDBusConnection::localMachineId() QT_END_NAMESPACE -#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 +#include "moc_qdbusconnection_p.cpp" +#include "moc_qdbusconnection.cpp" #endif // QT_NO_DBUS |