Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbusconnectionmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#include <qcoreapplication.h>
9#include <qthread.h>
10#include <qstringlist.h>
11#include <QtCore/private/qlocking_p.h>
12
13#include "qdbuserror.h"
16
17#ifndef QT_NO_DBUS
18
20
21#ifdef Q_OS_WIN
22static void preventDllUnload();
23#endif
24
25Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager)
26
27QDBusConnectionPrivate *QDBusConnectionManager::busConnection(QDBusConnection::BusType type)
28{
29 static_assert(int(QDBusConnection::SessionBus) + int(QDBusConnection::SystemBus) == 1);
30 Q_ASSERT(type == QDBusConnection::SessionBus || type == QDBusConnection::SystemBus);
31
32 if (!qdbus_loadLibDBus())
33 return nullptr;
34
35 // we'll start in suspended delivery mode if we're in the main thread
36 // (the event loop will resume delivery) and QCoreApplication already exists
37 bool suspendedDelivery = QThread::isMainThread() && qApp;
38
39 const auto locker = qt_scoped_lock(defaultBusMutex);
40 if (defaultBuses[type])
41 return defaultBuses[type];
42
43 QString name = QStringLiteral("qt_default_session_bus");
44 if (type == QDBusConnection::SystemBus)
45 name = QStringLiteral("qt_default_system_bus");
46 return defaultBuses[type] = connectToBus(type, name, suspendedDelivery);
47}
48
49QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const
50{
51 return connectionHash.value(name, nullptr);
52}
53
54QDBusConnectionPrivate *QDBusConnectionManager::existingConnection(const QString &name) const
55{
56 const auto locker = qt_scoped_lock(mutex);
57 auto *conn = connection(name);
58 if (conn)
59 conn->ref.ref();
60 return conn;
61}
62
63void QDBusConnectionManager::removeConnection(const QString &name)
64{
65 QDBusConnectionPrivate *d = nullptr;
66 d = connectionHash.take(name);
67 if (d && !d->ref.deref())
68 d->deleteLater();
69
70 // Static objects may be keeping the connection open.
71 // However, it is harmless to have outstanding references to a connection that is
72 // closing as long as those references will be soon dropped without being used.
73
74 // ### Output a warning if connections are being used after they have been removed.
75}
76
77void QDBusConnectionManager::removeConnections(const QStringList &names)
78{
79 const auto locker = qt_scoped_lock(mutex);
80
81 for (const auto &name : names)
82 removeConnection(name);
83}
84
85void QDBusConnectionManager::disconnectFrom(const QString &name,
86 QDBusConnectionPrivate::ConnectionMode mode)
87{
88 const auto locker = qt_scoped_lock(mutex);
89
90 QDBusConnectionPrivate *d = connection(name);
91 if (d && d->mode != mode)
92 return;
93 removeConnection(name);
94}
95
96QDBusConnectionManager::QDBusConnectionManager()
97{
98 // Ensure that the custom metatype registry is created before the instance
99 // of this class. This will ensure that the registry is not destroyed before
100 // the connection manager at application exit (see also QTBUG-58732). This
101 // works with compilers that use mechanism similar to atexit() to call
102 // destructurs for global statics.
104
105 moveToThread(this); // ugly, don't do this in other projects
106
107#ifdef Q_OS_WIN
108 // prevent the library from being unloaded on Windows. See comments in the function.
109 preventDllUnload();
110#endif
111 defaultBuses[0] = defaultBuses[1] = nullptr;
112 start();
113}
114
116{
117 quit();
118 wait();
119}
120
122{
123 return _q_manager();
124}
125
126Q_DBUS_EXPORT void qDBusBindToApplication();
128{
129}
130
131void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c)
132{
133 connectionHash[name] = c;
134 c->name = name;
135}
136
137void QDBusConnectionManager::addConnection(const QString &name, QDBusConnectionPrivate *c)
138{
139 const auto locker = qt_scoped_lock(mutex);
140 setConnection(name, c);
141}
142
144{
145 exec();
146
147 // cleanup:
148 const auto locker = qt_scoped_lock(mutex);
149 for (QDBusConnectionPrivate *d : std::as_const(connectionHash)) {
150 if (!d->ref.deref()) {
151 delete d;
152 } else {
153 d->closeConnection();
154 d->moveToThread(nullptr); // allow it to be deleted in another thread
155 }
156 }
157 connectionHash.clear();
158
159 // allow deletion from any thread without warning
160 moveToThread(nullptr);
161}
162
163QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(QDBusConnection::BusType type, const QString &name,
164 bool suspendedDelivery)
165{
166 QDBusConnectionPrivate *result = nullptr;
167
168 QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToStandardBus,
169 Qt::BlockingQueuedConnection, qReturnArg(result), type, name,
170 suspendedDelivery);
171
172 if (suspendedDelivery && result && result->connection)
173 result->enableDispatchDelayed(qApp); // qApp was checked in the caller
174
175 return result;
176}
177
178QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(const QString &address, const QString &name)
179{
180 QDBusConnectionPrivate *result = nullptr;
181
182 QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToBus,
183 Qt::BlockingQueuedConnection, qReturnArg(result), address, name);
184
185 return result;
186}
187
188QDBusConnectionPrivate *QDBusConnectionManager::connectToPeer(const QString &address, const QString &name)
189{
190 QDBusConnectionPrivate *result = nullptr;
191
192 QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToPeer,
193 Qt::BlockingQueuedConnection, qReturnArg(result), address, name);
194
195 return result;
196}
197
198QDBusConnectionPrivate *
199QDBusConnectionManager::doConnectToStandardBus(QDBusConnection::BusType type, const QString &name,
200 bool suspendedDelivery)
201{
202 const auto locker = qt_scoped_lock(mutex);
203
204 // check if the connection exists by name
205 QDBusConnectionPrivate *d = connection(name);
206 if (d || name.isEmpty())
207 return d;
208
209 d = new QDBusConnectionPrivate;
210 DBusConnection *c = nullptr;
211 QDBusErrorInternal error;
212
213 switch (type) {
214 case QDBusConnection::SystemBus:
215 c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error);
216 break;
217 case QDBusConnection::SessionBus:
218 c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error);
219 break;
220 case QDBusConnection::ActivationBus:
221 c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error);
222 break;
223 }
224
225 setConnection(name, d);
226
227 // create the bus service
228 // will lock in QDBusConnectionPrivate::connectRelay()
229 d->setConnection(c, error);
230 d->createBusService();
231 if (c && suspendedDelivery)
232 d->setDispatchEnabled(false);
233
234 return d;
235}
236
237QDBusConnectionPrivate *QDBusConnectionManager::doConnectToBus(const QString &address,
238 const QString &name)
239{
240 const auto locker = qt_scoped_lock(mutex);
241
242 // check if the connection exists by name
243 QDBusConnectionPrivate *d = connection(name);
244 if (d || name.isEmpty())
245 return d;
246
247 d = new QDBusConnectionPrivate;
248 QDBusErrorInternal error;
249
250 DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error);
251 if (c) {
252 // register on the bus
253 if (!q_dbus_bus_register(c, error)) {
254 q_dbus_connection_close(c);
255 q_dbus_connection_unref(c);
256 c = nullptr;
257 }
258 }
259
260 setConnection(name, d);
261
262 // create the bus service
263 // will lock in QDBusConnectionPrivate::connectRelay()
264 d->setConnection(c, error);
265 d->createBusService();
266
267 return d;
268}
269
270QDBusConnectionPrivate *QDBusConnectionManager::doConnectToPeer(const QString &address,
271 const QString &name)
272{
273 const auto locker = qt_scoped_lock(mutex);
274
275 // check if the connection exists by name
276 QDBusConnectionPrivate *d = connection(name);
277 if (d || name.isEmpty())
278 return d;
279
280 d = new QDBusConnectionPrivate;
281 QDBusErrorInternal error;
282
283 DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error);
284
285 setConnection(name, d);
286 d->setPeer(c, error);
287
288 return d;
289}
290
291void QDBusConnectionManager::createServer(const QString &address, QDBusServer *server)
292{
293 QMetaObject::invokeMethod(
294 this,
295 [&address, server] {
296 QDBusErrorInternal error;
297 QDBusConnectionPrivate *d = new QDBusConnectionPrivate;
298 d->setServer(server, q_dbus_server_listen(address.toUtf8().constData(), error),
299 error);
300 },
301 Qt::BlockingQueuedConnection);
302}
303
304QT_END_NAMESPACE
305
306#include "moc_qdbusconnectionmanager_p.cpp"
307
308#ifdef Q_OS_WIN
309# include <qt_windows.h>
310
311QT_BEGIN_NAMESPACE
312static void preventDllUnload()
313{
314 // Thread termination is really wacky on Windows. For some reason we don't
315 // understand, exiting from the thread may try to unload the DLL. Since the
316 // QDBusConnectionManager thread runs until the DLL is unloaded, we've got
317 // a deadlock: the main thread is waiting for the manager thread to exit,
318 // but the manager thread is attempting to acquire a lock to unload the DLL.
319 //
320 // We work around the issue by preventing the unload from happening in the
321 // first place.
322 //
323 // For this trick, see the blog post titled "What is the point of FreeLibraryAndExitThread?"
324 // https://devblogs.microsoft.com/oldnewthing/20131105-00/?p=2733
325
326 static HMODULE self;
327 GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
328 GET_MODULE_HANDLE_EX_FLAG_PIN,
329 reinterpret_cast<const wchar_t *>(&self), // any address in this DLL
330 &self);
331}
332QT_END_NAMESPACE
333#endif
334
335#endif // QT_NO_DBUS
QDBusConnectionPrivate * connectToBus(const QString &address, const QString &name)
static QDBusConnectionManager * instance()
void addConnection(const QString &name, QDBusConnectionPrivate *c)
void removeConnections(const QStringList &names)
QDBusConnectionPrivate * connectToPeer(const QString &address, const QString &name)
void createServer(const QString &address, QDBusServer *server)
QDBusConnectionPrivate * existingConnection(const QString &name) const
void disconnectFrom(const QString &name, QDBusConnectionPrivate::ConnectionMode mode)
Q_DBUS_EXPORT void init()
#define qApp
Q_DBUS_EXPORT void qDBusBindToApplication()