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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/dbus/qdbusconnectionmanager.cpp