Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/dbus/qdbusconnectionmanager.cpp
Line
Count
Source
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
6
#include "qdbusconnectionmanager_p.h"
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"
14
#include "qdbuspendingcall_p.h"
15
#include "qdbusmetatype_p.h"
16
17
#ifndef QT_NO_DBUS
18
19
QT_BEGIN_NAMESPACE
20
21
#ifdef Q_OS_WIN
22
static void preventDllUnload();
23
#endif
24
25
Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager)
26
27
QDBusConnectionPrivate *QDBusConnectionManager::busConnection(QDBusConnection::BusType type)
28
0
{
29
0
    static_assert(int(QDBusConnection::SessionBus) + int(QDBusConnection::SystemBus) == 1);
30
0
    Q_ASSERT(type == QDBusConnection::SessionBus || type == QDBusConnection::SystemBus);
31
32
0
    if (!qdbus_loadLibDBus())
33
0
        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
0
    bool suspendedDelivery = QThread::isMainThread() && qApp;
38
39
0
    const auto locker = qt_scoped_lock(defaultBusMutex);
40
0
    if (defaultBuses[type])
41
0
        return defaultBuses[type];
42
43
0
    QString name = QStringLiteral("qt_default_session_bus");
44
0
    if (type == QDBusConnection::SystemBus)
45
0
        name = QStringLiteral("qt_default_system_bus");
46
0
    return defaultBuses[type] = connectToBus(type, name, suspendedDelivery);
47
0
}
48
49
QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const
50
0
{
51
0
    return connectionHash.value(name, nullptr);
52
0
}
53
54
QDBusConnectionPrivate *QDBusConnectionManager::existingConnection(const QString &name) const
55
0
{
56
0
    const auto locker = qt_scoped_lock(mutex);
57
0
    auto *conn = connection(name);
58
0
    if (conn)
59
0
        conn->ref.ref();
60
0
    return conn;
61
0
}
62
63
void QDBusConnectionManager::removeConnection(const QString &name)
64
0
{
65
0
    QDBusConnectionPrivate *d = nullptr;
66
0
    d = connectionHash.take(name);
67
0
    if (d && !d->ref.deref())
68
0
        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
0
}
76
77
void QDBusConnectionManager::removeConnections(const QStringList &names)
78
0
{
79
0
    const auto locker = qt_scoped_lock(mutex);
80
81
0
    for (const auto &name : names)
82
0
        removeConnection(name);
83
0
}
84
85
void QDBusConnectionManager::disconnectFrom(const QString &name,
86
                                            QDBusConnectionPrivate::ConnectionMode mode)
87
0
{
88
0
    const auto locker = qt_scoped_lock(mutex);
89
90
0
    QDBusConnectionPrivate *d = connection(name);
91
0
    if (d && d->mode != mode)
92
0
        return;
93
0
    removeConnection(name);
94
0
}
95
96
QDBusConnectionManager::QDBusConnectionManager()
97
0
{
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.
103
0
    QDBusMetaTypeId::init();
104
105
0
    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
0
    defaultBuses[0] = defaultBuses[1] = nullptr;
112
0
    start();
113
0
}
114
115
QDBusConnectionManager::~QDBusConnectionManager()
116
0
{
117
0
    quit();
118
0
    wait();
119
0
}
120
121
QDBusConnectionManager* QDBusConnectionManager::instance()
122
0
{
123
0
    return _q_manager();
124
0
}
125
126
Q_DBUS_EXPORT void qDBusBindToApplication();
127
void qDBusBindToApplication()
128
0
{
129
0
}
130
131
void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c)
132
0
{
133
0
    connectionHash[name] = c;
134
0
    c->name = name;
135
0
}
136
137
void QDBusConnectionManager::addConnection(const QString &name, QDBusConnectionPrivate *c)
138
0
{
139
0
    const auto locker = qt_scoped_lock(mutex);
140
0
    setConnection(name, c);
141
0
}
142
143
void QDBusConnectionManager::run()
144
0
{
145
0
    exec();
146
147
    // cleanup:
148
0
    const auto locker = qt_scoped_lock(mutex);
149
0
    for (QDBusConnectionPrivate *d : std::as_const(connectionHash)) {
150
0
        if (!d->ref.deref()) {
151
0
            delete d;
152
0
        } else {
153
0
            d->closeConnection();
154
0
            d->moveToThread(nullptr);     // allow it to be deleted in another thread
155
0
        }
156
0
    }
157
0
    connectionHash.clear();
158
159
    // allow deletion from any thread without warning
160
0
    moveToThread(nullptr);
161
0
}
162
163
QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(QDBusConnection::BusType type, const QString &name,
164
                                                             bool suspendedDelivery)
165
0
{
166
0
    QDBusConnectionPrivate *result = nullptr;
167
168
0
    QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToStandardBus,
169
0
                              Qt::BlockingQueuedConnection, qReturnArg(result), type, name,
170
0
                              suspendedDelivery);
171
172
0
    if (suspendedDelivery && result && result->connection)
173
0
        result->enableDispatchDelayed(qApp); // qApp was checked in the caller
174
175
0
    return result;
176
0
}
177
178
QDBusConnectionPrivate *QDBusConnectionManager::connectToBus(const QString &address, const QString &name)
179
0
{
180
0
    QDBusConnectionPrivate *result = nullptr;
181
182
0
    QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToBus,
183
0
                              Qt::BlockingQueuedConnection, qReturnArg(result), address, name);
184
185
0
    return result;
186
0
}
187
188
QDBusConnectionPrivate *QDBusConnectionManager::connectToPeer(const QString &address, const QString &name)
189
0
{
190
0
    QDBusConnectionPrivate *result = nullptr;
191
192
0
    QMetaObject::invokeMethod(this, &QDBusConnectionManager::doConnectToPeer,
193
0
                              Qt::BlockingQueuedConnection, qReturnArg(result), address, name);
194
195
0
    return result;
196
0
}
197
198
QDBusConnectionPrivate *
199
QDBusConnectionManager::doConnectToStandardBus(QDBusConnection::BusType type, const QString &name,
200
                                               bool suspendedDelivery)
201
0
{
202
0
    const auto locker = qt_scoped_lock(mutex);
203
204
    // check if the connection exists by name
205
0
    QDBusConnectionPrivate *d = connection(name);
206
0
    if (d || name.isEmpty())
207
0
        return d;
208
209
0
    d = new QDBusConnectionPrivate;
210
0
    DBusConnection *c = nullptr;
211
0
    QDBusErrorInternal error;
212
213
0
    switch (type) {
214
0
    case QDBusConnection::SystemBus:
215
0
        c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error);
216
0
        break;
217
0
    case QDBusConnection::SessionBus:
218
0
        c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error);
219
0
        break;
220
0
    case QDBusConnection::ActivationBus:
221
0
        c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error);
222
0
        break;
223
0
    }
224
225
0
    setConnection(name, d);
226
227
    // create the bus service
228
    // will lock in QDBusConnectionPrivate::connectRelay()
229
0
    d->setConnection(c, error);
230
0
    d->createBusService();
231
0
    if (c && suspendedDelivery)
232
0
        d->setDispatchEnabled(false);
233
234
0
    return d;
235
0
}
236
237
QDBusConnectionPrivate *QDBusConnectionManager::doConnectToBus(const QString &address,
238
                                                               const QString &name)
239
0
{
240
0
    const auto locker = qt_scoped_lock(mutex);
241
242
    // check if the connection exists by name
243
0
    QDBusConnectionPrivate *d = connection(name);
244
0
    if (d || name.isEmpty())
245
0
        return d;
246
247
0
    d = new QDBusConnectionPrivate;
248
0
    QDBusErrorInternal error;
249
250
0
    DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error);
251
0
    if (c) {
252
        // register on the bus
253
0
        if (!q_dbus_bus_register(c, error)) {
254
0
            q_dbus_connection_close(c);
255
0
            q_dbus_connection_unref(c);
256
0
            c = nullptr;
257
0
        }
258
0
    }
259
260
0
    setConnection(name, d);
261
262
    // create the bus service
263
    // will lock in QDBusConnectionPrivate::connectRelay()
264
0
    d->setConnection(c, error);
265
0
    d->createBusService();
266
267
0
    return d;
268
0
}
269
270
QDBusConnectionPrivate *QDBusConnectionManager::doConnectToPeer(const QString &address,
271
                                                                const QString &name)
272
0
{
273
0
    const auto locker = qt_scoped_lock(mutex);
274
275
    // check if the connection exists by name
276
0
    QDBusConnectionPrivate *d = connection(name);
277
0
    if (d || name.isEmpty())
278
0
        return d;
279
280
0
    d = new QDBusConnectionPrivate;
281
0
    QDBusErrorInternal error;
282
283
0
    DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error);
284
285
0
    setConnection(name, d);
286
0
    d->setPeer(c, error);
287
288
0
    return d;
289
0
}
290
291
void QDBusConnectionManager::createServer(const QString &address, QDBusServer *server)
292
0
{
293
0
    QMetaObject::invokeMethod(
294
0
            this,
295
0
            [&address, server] {
296
0
                QDBusErrorInternal error;
297
0
                QDBusConnectionPrivate *d = new QDBusConnectionPrivate;
298
0
                d->setServer(server, q_dbus_server_listen(address.toUtf8().constData(), error),
299
0
                             error);
300
0
            },
301
0
            Qt::BlockingQueuedConnection);
302
0
}
303
304
QT_END_NAMESPACE
305
306
#include "moc_qdbusconnectionmanager_p.cpp"
307
308
#ifdef Q_OS_WIN
309
#  include <qt_windows.h>
310
311
QT_BEGIN_NAMESPACE
312
static 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
}
332
QT_END_NAMESPACE
333
#endif
334
335
#endif // QT_NO_DBUS