| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qdbusconnection.h" |
| #include "qdbusconnection_p.h" |
| |
| #include <qdebug.h> |
| #include <qcoreapplication.h> |
| #include <qstringlist.h> |
| #include <qvector.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> |
| |
| #ifdef interface |
| #undef interface |
| #endif |
| |
| #ifndef QT_NO_DBUS |
| |
| QT_BEGIN_NAMESPACE |
| |
| #ifdef Q_OS_WIN |
| static void preventDllUnload(); |
| #endif |
| |
| Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager) |
| |
| struct QDBusConnectionManager::ConnectionRequestData |
| { |
| enum RequestType { |
| ConnectToStandardBus, |
| ConnectToBusByAddress, |
| ConnectToPeerByAddress |
| } type; |
| |
| union { |
| QDBusConnection::BusType busType; |
| const QString *busAddress; |
| }; |
| const QString *name; |
| |
| QDBusConnectionPrivate *result; |
| |
| bool suspendedDelivery; |
| }; |
| |
| QDBusConnectionPrivate *QDBusConnectionManager::busConnection(QDBusConnection::BusType type) |
| { |
| Q_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, 0); |
| } |
| |
| 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 |
| \since 4.2 |
| |
| \brief The QDBusConnection class represents a connection to the D-Bus bus daemon. |
| |
| This class is the initial point in a D-Bus session. Using it, you |
| can get access to remote objects, interfaces; connect remote |
| signals to your object's slots; register objects, etc. |
| |
| D-Bus connections are created using the connectToBus() function, |
| which opens a connection to the server daemon and does the initial |
| handshaking, associating that connection with a name. Further |
| attempts to connect using the same name will return the same |
| connection. |
| |
| The connection is then torn down using the disconnectFromBus() |
| function. |
| |
| Once disconnected, calling connectToBus() will not reestablish a |
| connection, you must create a new QDBusConnection instance. |
| |
| As a convenience for the two most common connection types, the |
| sessionBus() and systemBus() functions return open connections to |
| the session server daemon and the system server daemon, |
| respectively. Those connections are opened when first used and are |
| closed when the QCoreApplication destructor is run. |
| |
| D-Bus also supports peer-to-peer connections, without the need for |
| a bus server daemon. Using this facility, two applications can |
| talk to each other and exchange messages. This can be achieved by |
| passing an address to connectToBus() function, which was opened by |
| another D-Bus application using QDBusServer. |
| */ |
| |
| /*! |
| \enum QDBusConnection::BusType |
| Specifies the type of the bus connection. The valid bus types are: |
| |
| \value SessionBus the session bus, associated with the running desktop session |
| \value SystemBus the system bus, used to communicate with system-wide processes |
| \value ActivationBus the activation bus, the "alias" for the bus that started the |
| service |
| |
| On the Session Bus, one can find other applications by the same user that are sharing the same |
| desktop session (hence the name). On the System Bus, however, processes shared for the whole |
| system are usually found. |
| */ |
| |
| /*! |
| \enum QDBusConnection::RegisterOption |
| Specifies the options for registering objects with the connection. The possible values are: |
| |
| \value ExportAdaptors export the contents of adaptors found in this object |
| |
| \value ExportScriptableSlots export this object's scriptable slots |
| \value ExportScriptableSignals export this object's scriptable signals |
| \value ExportScriptableProperties export this object's scriptable properties |
| \value ExportScriptableInvokables export this object's scriptable invokables |
| \value ExportScriptableContents shorthand form for ExportScriptableSlots | |
| ExportScriptableSignals | |
| ExportScriptableProperties |
| |
| \value ExportNonScriptableSlots export this object's non-scriptable slots |
| \value ExportNonScriptableSignals export this object's non-scriptable signals |
| \value ExportNonScriptableProperties export this object's non-scriptable properties |
| \value ExportNonScriptableInvokables export this object's non-scriptable invokables |
| \value ExportNonScriptableContents shorthand form for ExportNonScriptableSlots | |
| ExportNonScriptableSignals | |
| ExportNonScriptableProperties |
| |
| \value ExportAllSlots export all of this object's slots |
| \value ExportAllSignals export all of this object's signals |
| \value ExportAllProperties export all of this object's properties |
| \value ExportAllInvokables export all of this object's invokables |
| \value ExportAllContents export all of this object's contents |
| \value ExportChildObjects export this object's child objects |
| |
| \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors} |
| */ |
| |
| /*! |
| \internal |
| \since 4.8 |
| \enum QDBusConnection::VirtualObjectRegisterOption |
| Specifies the options for registering virtual objects with the connection. The possible values are: |
| |
| \value SingleNode register a virtual object to handle one path only |
| \value SubPath register a virtual object so that it handles all sub paths |
| |
| \sa registerVirtualObject(), QDBusVirtualObject |
| */ |
| |
| /*! |
| \enum QDBusConnection::UnregisterMode |
| The mode for unregistering an object path: |
| |
| \value UnregisterNode unregister this node only: do not unregister child objects |
| \value UnregisterTree unregister this node and all its sub-tree |
| |
| Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode |
| will unregister the child objects too. |
| */ |
| |
| /*! |
| \since 4.8 |
| \enum QDBusConnection::ConnectionCapability |
| |
| This enum describes the available capabilities for a D-Bus connection. |
| |
| \value UnixFileDescriptorPassing enables passing of Unix file descriptors to other processes |
| (see QDBusUnixFileDescriptor) |
| |
| \sa connectionCapabilities() |
| */ |
| |
| /*! |
| Creates a QDBusConnection object attached to the connection with name \a name. |
| |
| This does not open the connection. You have to call connectToBus() to open it. |
| */ |
| QDBusConnection::QDBusConnection(const QString &name) |
| { |
| if (name.isEmpty() || _q_manager.isDestroyed()) { |
| d = nullptr; |
| } else { |
| const auto locker = qt_scoped_lock(_q_manager()->mutex); |
| d = _q_manager()->connection(name); |
| if (d) |
| d->ref.ref(); |
| } |
| } |
| |
| /*! |
| Creates a copy of the \a other connection. |
| */ |
| QDBusConnection::QDBusConnection(const QDBusConnection &other) |
| { |
| d = other.d; |
| if (d) |
| d->ref.ref(); |
| } |
| |
| /*! |
| \internal |
| Creates a connection object with the given \a dd as private object. |
| */ |
| QDBusConnection::QDBusConnection(QDBusConnectionPrivate *dd) |
| { |
| d = dd; |
| if (d) |
| d->ref.ref(); |
| } |
| |
| /*! |
| Disposes of this object. This does not close the connection: you |
| have to call disconnectFromBus() to do that. |
| */ |
| QDBusConnection::~QDBusConnection() |
| { |
| if (d && !d->ref.deref()) |
| d->deleteLater(); |
| } |
| |
| /*! |
| Creates a copy of the connection \a other in this object. Note |
| that the connection this object referenced before the copy, is not |
| spontaneously disconnected. |
| |
| \sa disconnectFromBus() |
| */ |
| QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other) |
| { |
| if (other.d) |
| other.d->ref.ref(); |
| if (d && !d->ref.deref()) |
| d->deleteLater(); |
| d = other.d; |
| return *this; |
| } |
| |
| /*! |
| Opens a connection of type \a type to one of the known busses 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()) { |
| QDBusConnectionPrivate *d = nullptr; |
| return QDBusConnection(d); |
| } |
| return QDBusConnection(_q_manager()->connectToBus(type, name, false)); |
| } |
| |
| /*! |
| Opens a connection to a private bus on address \a address and associate with it the |
| connection name \a name. Returns a QDBusConnection object associated with that connection. |
| */ |
| QDBusConnection QDBusConnection::connectToBus(const QString &address, |
| const QString &name) |
| { |
| if (_q_manager.isDestroyed() || !qdbus_loadLibDBus()) { |
| QDBusConnectionPrivate *d = nullptr; |
| return QDBusConnection(d); |
| } |
| return QDBusConnection(_q_manager()->connectToBus(address, name)); |
| } |
| /*! |
| \since 4.8 |
| |
| Opens a peer-to-peer connection on address \a address and associate with it the |
| connection name \a name. Returns a QDBusConnection object associated with that connection. |
| */ |
| QDBusConnection QDBusConnection::connectToPeer(const QString &address, |
| const QString &name) |
| { |
| if (_q_manager.isDestroyed() || !qdbus_loadLibDBus()) { |
| QDBusConnectionPrivate *d = nullptr; |
| return QDBusConnection(d); |
| } |
| return QDBusConnection(_q_manager()->connectToPeer(address, name)); |
| } |
| |
| /*! |
| Closes the bus connection of name \a name. |
| |
| Note that if there are still QDBusConnection objects associated |
| with the same connection, the connection will not be closed until |
| all references are dropped. However, no further references can be |
| created using the QDBusConnection constructor. |
| */ |
| 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); |
| } |
| } |
| |
| /*! |
| \since 4.8 |
| |
| Closes the peer connection of name \a name. |
| |
| Note that if there are still QDBusConnection objects associated |
| with the same connection, the connection will not be closed until |
| all references are dropped. However, no further references can be |
| created using the QDBusConnection constructor. |
| */ |
| 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); |
| } |
| } |
| |
| /*! |
| Sends the \a message over this connection, without waiting for a |
| reply. This is suitable for errors, signals, and return values as |
| well as calls whose return values are not necessary. |
| |
| Returns \c true if the message was queued successfully, false otherwise. |
| */ |
| bool QDBusConnection::send(const QDBusMessage &message) const |
| { |
| if (!d || !d->connection) { |
| QDBusError err = QDBusError(QDBusError::Disconnected, |
| QDBusUtil::disconnectedErrorMessage()); |
| if (d) |
| d->lastError = err; |
| return false; |
| } |
| return d->send(message); |
| } |
| |
| /*! |
| Sends the \a message over this connection and returns immediately. |
| When the reply is received, the method \a returnMethod is called in |
| the \a receiver object. If an error occurs, the method \a errorMethod |
| will be called instead. |
| |
| If no reply is received within \a timeout milliseconds, an automatic |
| error will be delivered indicating the expiration of the call. |
| The default \a timeout is -1, which will be replaced with an |
| implementation-defined value that is suitable for inter-process |
| communications (generally, 25 seconds). |
| |
| This function is suitable for method calls only. It is guaranteed |
| that the slot will be called exactly once with the reply, as long |
| as the parameter types match and no error occurs. |
| |
| Returns \c true if the message was sent, or false if the message could |
| not be sent. |
| */ |
| bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, |
| const char *returnMethod, const char *errorMethod, |
| int timeout) const |
| { |
| if (!d || !d->connection) { |
| QDBusError err = QDBusError(QDBusError::Disconnected, |
| QDBusUtil::disconnectedErrorMessage()); |
| if (d) |
| d->lastError = err; |
| return false; |
| } |
| return d->sendWithReplyAsync(message, receiver, returnMethod, errorMethod, timeout) != nullptr; |
| } |
| |
| /*! |
| \overload |
| \deprecated |
| Sends the \a message over this connection and returns immediately. |
| When the reply is received, the method \a returnMethod is called in |
| the \a receiver object. |
| |
| This function is suitable for method calls only. It is guaranteed |
| that the slot will be called exactly once with the reply, as long |
| as the parameter types match and no error occurs. |
| |
| This function is dangerous because it cannot report errors, including |
| the expiration of the timeout. |
| |
| Returns \c true if the message was sent, or false if the message could |
| not be sent. |
| */ |
| bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, |
| const char *returnMethod, int timeout) const |
| { |
| return callWithCallback(message, receiver, returnMethod, nullptr, timeout); |
| } |
| |
| /*! |
| Sends the \a message over this connection and blocks, waiting for |
| a reply, for at most \a timeout milliseconds. This function is |
| suitable for method calls only. It returns the reply message as |
| its return value, which will be either of type |
| QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage. |
| |
| If no reply is received within \a timeout milliseconds, an automatic |
| error will be delivered indicating the expiration of the call. |
| The default \a timeout is -1, which will be replaced with an |
| implementation-defined value that is suitable for inter-process |
| communications (generally, 25 seconds). |
| |
| See the QDBusInterface::call() function for a more friendly way |
| of placing calls. |
| |
| \warning If \a mode is QDBus::BlockWithGui, this function will |
| reenter the Qt event loop in order to wait for the |
| reply. During the wait, it may deliver signals and other |
| method calls to your application. Therefore, it must be |
| prepared to handle a reentrancy whenever a call is |
| placed with call(). |
| */ |
| QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const |
| { |
| if (!d || !d->connection) { |
| QDBusError err = QDBusError(QDBusError::Disconnected, |
| QDBusUtil::disconnectedErrorMessage()); |
| if (d) |
| d->lastError = err; |
| |
| return QDBusMessage::createError(err); |
| } |
| |
| if (mode != QDBus::NoBlock) |
| return d->sendWithReply(message, mode, timeout); |
| |
| d->send(message); |
| QDBusMessage retval; |
| retval << QVariant(); // add one argument (to avoid .at(0) problems) |
| return retval; |
| } |
| |
| /*! |
| \since 4.5 |
| Sends the \a message over this connection and returns |
| immediately. This function is suitable for method calls only. It |
| returns an object of type QDBusPendingCall which can be used to |
| track the status of the reply. |
| |
| If no reply is received within \a timeout milliseconds, an automatic |
| error will be delivered indicating the expiration of the call. The |
| default \a timeout is -1, which will be replaced with an |
| implementation-defined value that is suitable for inter-process |
| communications (generally, 25 seconds). This timeout is also the |
| upper limit for waiting in QDBusPendingCall::waitForFinished(). |
| |
| See the QDBusInterface::asyncCall() function for a more friendly way |
| of placing calls. |
| */ |
| QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int timeout) const |
| { |
| if (!d || !d->connection) { |
| return QDBusPendingCall(nullptr); // null pointer -> disconnected |
| } |
| |
| QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, nullptr, nullptr, nullptr, timeout); |
| return QDBusPendingCall(priv); |
| } |
| |
| /*! |
| Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to |
| the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, |
| denoting a connection to any signal of the (\a interface, \a name) pair, from any remote |
| application. |
| |
| Returns \c true if the connection was successful. |
| |
| \warning The signal will only be delivered to the slot if the parameters match. This verification |
| can be done only when the signal is received, not at connection time. |
| */ |
| bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
| const QString &name, QObject *receiver, const char *slot) |
| { |
| return connect(service, path, interface, name, QStringList(), QString(), receiver, slot); |
| } |
| |
| /*! |
| \overload |
| |
| Connects the signal to the slot \a slot in object \a |
| receiver. Unlike the previous connect() overload, this function |
| allows one to specify the parameter signature to be connected |
| using the \a signature variable. The function will then verify |
| that this signature can be delivered to the slot specified by \a |
| slot and return false otherwise. |
| |
| Returns \c true if the connection was successful. |
| |
| \note This function verifies that the signal signature matches the |
| slot's parameters, but it does not verify that the actual |
| signal exists with the given signature in the remote |
| service. |
| */ |
| bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
| const QString &name, const QString &signature, |
| QObject *receiver, const char *slot) |
| { |
| return connect(service, path, interface, name, QStringList(), signature, receiver, slot); |
| } |
| |
| /*! |
| \overload |
| \since 4.6 |
| |
| Connects the signal to the slot \a slot in object \a |
| receiver. Unlike the previous connect() overload, this function |
| allows one to specify the parameter signature to be connected |
| using the \a signature variable. The function will then verify |
| that this signature can be delivered to the slot specified by \a |
| slot and return false otherwise. |
| |
| The \a argumentMatch parameter lists the string parameters to be matched, |
| in sequential order. Note that, to match an empty string, you need to |
| pass a QString that is empty but not null (i.e., QString("")). A null |
| QString skips matching at that position. |
| |
| Returns \c true if the connection was successful. |
| |
| \note This function verifies that the signal signature matches the |
| slot's parameters, but it does not verify that the actual |
| signal exists with the given signature in the remote |
| service. |
| */ |
| bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
| const QString &name, const QStringList &argumentMatch, const QString &signature, |
| QObject *receiver, const char *slot) |
| { |
| |
| if (!receiver || !slot || !d || !d->connection) |
| return false; |
| if (interface.isEmpty() && name.isEmpty()) |
| return false; |
| if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) { |
| #ifndef QT_NO_DEBUG |
| qWarning("QDBusConnection::connect: interface name '%s' is not valid", interface.toLatin1().constData()); |
| #endif |
| return false; |
| } |
| if (!service.isEmpty() && !QDBusUtil::isValidBusName(service)) { |
| #ifndef QT_NO_DEBUG |
| qWarning("QDBusConnection::connect: service name '%s' is not valid", service.toLatin1().constData()); |
| #endif |
| return false; |
| } |
| if (!path.isEmpty() && !QDBusUtil::isValidObjectPath(path)) { |
| #ifndef QT_NO_DEBUG |
| qWarning("QDBusConnection::connect: object path '%s' is not valid", path.toLatin1().constData()); |
| #endif |
| return false; |
| } |
| |
| return d->connectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); |
| } |
| |
| /*! |
| Disconnects the signal specified by the \a service, \a path, \a interface |
| and \a name parameters from the slot \a slot in object \a receiver. The |
| arguments must be the same as passed to the connect() function. |
| |
| Returns \c true if the disconnection was successful. |
| */ |
| bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface, |
| const QString &name, QObject *receiver, const char *slot) |
| { |
| return disconnect(service, path, interface, name, QStringList(), QString(), receiver, slot); |
| } |
| |
| /*! |
| \overload |
| |
| Disconnects the signal specified by the \a service, \a path, \a |
| interface, \a name, and \a signature parameters from the slot \a slot in |
| object \a receiver. The arguments must be the same as passed to the |
| connect() function. |
| |
| Returns \c true if the disconnection was successful. |
| */ |
| bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, |
| const QString &name, const QString &signature, |
| QObject *receiver, const char *slot) |
| { |
| return disconnect(service, path, interface, name, QStringList(), signature, receiver, slot); |
| } |
| |
| /*! |
| \overload |
| \since 4.6 |
| |
| Disconnects the signal specified by the \a service, \a path, \a |
| interface, \a name, \a argumentMatch, and \a signature parameters from |
| the slot \a slot in object \a receiver. The arguments must be the same as |
| passed to the connect() function. |
| |
| Returns \c true if the disconnection was successful. |
| */ |
| bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, |
| const QString &name, const QStringList &argumentMatch, const QString &signature, |
| QObject *receiver, const char *slot) |
| { |
| if (!receiver || !slot || !d || !d->connection) |
| return false; |
| if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) |
| return false; |
| if (interface.isEmpty() && name.isEmpty()) |
| return false; |
| |
| return d->disconnectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); |
| } |
| |
| /*! |
| Registers the object \a object at path \a path and returns \c true if |
| the registration was successful. The \a options parameter |
| specifies how much of the object \a object will be exposed through |
| D-Bus. |
| |
| This function does not replace existing objects: if there is already an object registered at |
| path \a path, this function will return false. Use unregisterObject() to unregister it first. |
| |
| The ExportChildObjects flag exports child objects on D-Bus based on the |
| path of the registered objects and the QObject::objectName of the child. |
| Therefore, it is important for the child object to have an object name. |
| |
| You cannot register an object as a child object of an object that |
| was registered with ExportChildObjects. |
| */ |
| bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options) |
| { |
| return registerObject(path, QString(), object, options); |
| } |
| |
| /*! |
| \overload |
| \since 5.5 |
| |
| Registers the object \a object at path \a path with interface name \a interface |
| and returns \c true if the registration was successful. The \a options parameter |
| specifies how much of the object \a object will be exposed through |
| D-Bus. |
| |
| This function does not replace existing objects: if there is already an object registered at |
| path \a path, this function will return false. Use unregisterObject() to unregister it first. |
| |
| The ExportChildObjects flag exports child objects on D-Bus based on the |
| path of the registered objects and the QObject::objectName of the child. |
| Therefore, it is important for the child object to have an object name. |
| |
| You cannot register an object as a child object of an object that |
| was registered with ExportChildObjects. |
| */ |
| bool QDBusConnection::registerObject(const QString &path, const QString &interface, QObject *object, RegisterOptions options) |
| { |
| Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registerObject", |
| "Invalid object path given"); |
| if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) |
| return false; |
| |
| auto pathComponents = path.splitRef(QLatin1Char('/')); |
| if (pathComponents.constLast().isEmpty()) |
| pathComponents.removeLast(); |
| QDBusWriteLocker locker(RegisterObjectAction, d); |
| |
| // lower-bound search for where this object should enter in the tree |
| QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator node = &d->rootNode; |
| int i = 1; |
| while (node) { |
| if (pathComponents.count() == 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 |
| if (node->obj) |
| return false; |
| |
| if (options & QDBusConnectionPrivate::VirtualObject) { |
| if (options & SubPath && !node->children.isEmpty()) |
| return false; |
| } else { |
| if ((options & ExportChildObjects && !node->children.isEmpty())) |
| return false; |
| } |
| // we can add the object here |
| node->obj = object; |
| node->flags = options; |
| node->interfaceName = interface; |
| |
| d->registerObject(node); |
| //qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData()); |
| return true; |
| } |
| |
| // if a virtual object occupies this path, return false |
| if (node->obj && (node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) { |
| //qDebug("Cannot register object at %s because QDBusVirtualObject handles all sub-paths.", |
| // qPrintable(path)); |
| return false; |
| } |
| |
| // find the position where we'd insert the node |
| QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = |
| std::lower_bound(node->children.begin(), node->children.end(), pathComponents.at(i)); |
| if (it != node->children.end() && it->name == pathComponents.at(i)) { |
| // match: this node exists |
| node = it; |
| |
| // are we allowed to go deeper? |
| if (node->flags & ExportChildObjects) { |
| // we're not |
| //qDebug("Cannot register object at %s because %s exports its own child objects", |
| // qPrintable(path), qPrintable(pathComponents.at(i))); |
| return false; |
| } |
| } else { |
| // add entry |
| node = node->children.insert(it, pathComponents.at(i).toString()); |
| } |
| |
| // iterate |
| ++i; |
| } |
| |
| Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened"); |
| return false; |
| } |
| |
| /*! |
| \internal |
| \since 4.8 |
| Registers a QDBusTreeNode for a path. It can handle a path including all child paths, thus |
| handling multiple DBus nodes. |
| |
| To unregister a QDBusTreeNode use the unregisterObject() function with its path. |
| */ |
| bool QDBusConnection::registerVirtualObject(const QString &path, QDBusVirtualObject *treeNode, |
| VirtualObjectRegisterOption options) |
| { |
| int opts = options | QDBusConnectionPrivate::VirtualObject; |
| return registerObject(path, (QObject*) treeNode, (RegisterOptions) opts); |
| } |
| |
| /*! |
| Unregisters an object that was registered with the registerObject() at the object path given by |
| \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too. |
| |
| Note that you cannot unregister objects that were not registered with registerObject(). |
| */ |
| void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode) |
| { |
| if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) |
| return; |
| |
| QDBusWriteLocker locker(UnregisterObjectAction, d); |
| d->unregisterObject(path, mode); |
| } |
| |
| /*! |
| Return the object that was registered with the registerObject() at the object path given by |
| \a path. |
| */ |
| QObject *QDBusConnection::objectRegisteredAt(const QString &path) const |
| { |
| Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registeredObject", |
| "Invalid object path given"); |
| if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) |
| return nullptr; |
| |
| auto pathComponents = path.splitRef(QLatin1Char('/')); |
| if (pathComponents.constLast().isEmpty()) |
| pathComponents.removeLast(); |
| |
| // lower-bound search for where this object should enter in the tree |
| QDBusReadLocker lock(ObjectRegisteredAtAction, d); |
| const QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; |
| |
| int i = 1; |
| while (node) { |
| if (pathComponents.count() == i) |
| return node->obj; |
| if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) |
| return node->obj; |
| |
| QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = |
| std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i)); |
| if (it == node->children.constEnd() || it->name != pathComponents.at(i)) |
| break; // node not found |
| |
| node = it; |
| ++i; |
| } |
| return nullptr; |
| } |
| |
| |
| |
| /*! |
| Returns a QDBusConnectionInterface object that represents the |
| D-Bus server interface on this connection. |
| */ |
| QDBusConnectionInterface *QDBusConnection::interface() const |
| { |
| if (!d || d->mode != QDBusConnectionPrivate::ClientMode) |
| return nullptr; |
| return d->busService; |
| } |
| |
| /*! |
| \internal |
| \since 4.8 |
| |
| Returns the internal, implementation-defined pointer for this |
| connection. Currently, this returns a DBusConnection* pointer, |
| without changing the reference count. It is the responsibility of |
| the caller to call dbus_connection_ref if it wants to store the |
| pointer. |
| */ |
| void *QDBusConnection::internalPointer() const |
| { |
| return d ? d->connection : nullptr; |
| } |
| |
| /*! |
| Returns \c true if this QDBusConnection object is connected. |
| */ |
| bool QDBusConnection::isConnected() const |
| { |
| return d && d->connection && q_dbus_connection_get_is_connected(d->connection); |
| } |
| |
| /*! |
| Returns the last error that happened in this connection. |
| |
| This function is provided for low-level code. If you're using |
| QDBusInterface::call(), error codes are reported by its return |
| value. |
| |
| \sa QDBusInterface, QDBusMessage |
| */ |
| QDBusError QDBusConnection::lastError() const |
| { |
| return d ? d->lastError : QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage()); |
| } |
| |
| /*! |
| Returns the unique connection name for this connection, if this QDBusConnection object is |
| connected, or an empty QString otherwise. |
| |
| A Unique Connection Name is a string in the form ":x.xxx" (where x |
| are decimal digits) that is assigned by the D-Bus server daemon |
| upon connection. It uniquely identifies this client in the bus. |
| |
| This function returns an empty QString for peer-to-peer connections. |
| */ |
| QString QDBusConnection::baseService() const |
| { |
| return d ? d->baseService : QString(); |
| } |
| |
| /*! |
| \since 4.5 |
| |
| Returns the connection name for this connection, as given as the |
| name parameter to connectToBus(). |
| |
| The connection name can be used to uniquely identify actual |
| underlying connections to buses. Copies made from a single |
| connection will always implicitly share the underlying connection, |
| and hence will have the same connection name. |
| |
| Inversely, two connections having different connection names will |
| always either be connected to different buses, or have a different |
| unique name (as returned by baseService()) on that bus. |
| |
| \sa connectToBus(), disconnectFromBus() |
| */ |
| QString QDBusConnection::name() const |
| { |
| return d ? d->name : QString(); |
| } |
| |
| /*! |
| \since 4.8 |
| |
| Returns the capabilities of this connection as negotiated with the bus |
| server or peer. If this QDBusConnection is not connected, this function |
| returns no capabilities. |
| */ |
| QDBusConnection::ConnectionCapabilities QDBusConnection::connectionCapabilities() const |
| { |
| return d ? d->connectionCapabilities() : ConnectionCapabilities(); |
| } |
| |
| /*! |
| Attempts to register the \a serviceName on the D-Bus server and |
| returns \c true if the registration succeeded. The registration will |
| fail if the name is already registered by another application. |
| |
| \sa unregisterService(), QDBusConnectionInterface::registerService() |
| */ |
| bool QDBusConnection::registerService(const QString &serviceName) |
| { |
| if (interface() && interface()->registerService(serviceName)) { |
| if (d) d->registerService(serviceName); |
| return true; |
| } |
| return false; |
| } |
| |
| /*! |
| Unregisters the service \a serviceName that was previously |
| registered with registerService() and returns \c true if it |
| succeeded. |
| |
| \sa registerService(), QDBusConnectionInterface::unregisterService() |
| */ |
| bool QDBusConnection::unregisterService(const QString &serviceName) |
| { |
| if (interface()->unregisterService(serviceName)) { |
| if (d) d->unregisterService(serviceName); |
| return true; |
| } |
| return false; |
| } |
| |
| /*! |
| \fn QDBusConnection QDBusConnection::sessionBus() |
| |
| Returns a QDBusConnection object opened with the session bus. The object |
| reference returned by this function is valid until the application terminates, |
| at which point the connection will be closed and the object deleted. |
| */ |
| QDBusConnection QDBusConnection::sessionBus() |
| { |
| if (_q_manager.isDestroyed()) |
| return QDBusConnection(nullptr); |
| return QDBusConnection(_q_manager()->busConnection(SessionBus)); |
| } |
| |
| /*! |
| \fn QDBusConnection QDBusConnection::systemBus() |
| |
| Returns a QDBusConnection object opened with the system bus. The object reference returned |
| by this function is valid until the QCoreApplication's destructor is run, when the |
| connection will be closed and the object, deleted. |
| */ |
| QDBusConnection QDBusConnection::systemBus() |
| { |
| if (_q_manager.isDestroyed()) |
| return QDBusConnection(nullptr); |
| return QDBusConnection(_q_manager()->busConnection(SystemBus)); |
| } |
| |
| #if QT_DEPRECATED_SINCE(5,5) |
| /*! |
| \deprecated |
| |
| Always returns a disconnected, invalid QDBusConnection object. For the old |
| functionality of determining the sender connection, please use QDBusContext. |
| |
| \sa QDBusContext |
| */ |
| QDBusConnection QDBusConnection::sender() |
| { |
| return QDBusConnection(QString()); |
| } |
| #endif |
| |
| /*! |
| \internal |
| */ |
| void QDBusConnectionPrivate::createBusService() |
| { |
| Q_ASSERT(mode == ClientMode); |
| QDBusConnection connection(this); |
| busService = new QDBusConnectionInterface(connection, this); |
| ref.deref(); // busService has increased the refcounting to us |
| // avoid cyclic refcounting |
| |
| QObject::connect(this, &QDBusConnectionPrivate::callWithCallbackFailed, |
| busService, emit &QDBusConnectionInterface::callWithCallbackFailed, |
| Qt::QueuedConnection); |
| } |
| |
| /*! |
| \since 4.8 |
| Returns the local machine ID as known to the D-Bus system. Each |
| node or host that runs D-Bus has a unique identifier that can be |
| used to distinguish it from other hosts if they are sharing |
| resources like the filesystem. |
| |
| Note that the local machine ID is not guaranteed to be persistent |
| across boots of the system, so this identifier should not be |
| stored in persistent storage (like the filesystem). It is |
| guaranteed to remain constant only during the lifetime of this |
| boot session. |
| */ |
| QByteArray QDBusConnection::localMachineId() |
| { |
| char *dbus_machine_id = q_dbus_get_local_machine_id(); |
| QByteArray result = dbus_machine_id; |
| q_dbus_free(dbus_machine_id); |
| return result; |
| } |
| |
| /*! |
| \namespace QDBus |
| \inmodule QtDBus |
| |
| \brief The QDBus namespace contains miscellaneous identifiers used |
| throughout the Qt D-Bus module. |
| */ |
| |
| /*! |
| \enum QDBus::CallMode |
| |
| This enum describes the various ways of placing a function call. The valid modes are: |
| |
| \value NoBlock Place the call but don't wait for the reply (the reply's contents |
| will be discarded). |
| \value Block Don't use an event loop to wait for a reply, but instead block on |
| network operations while waiting. This means the |
| user-interface may not be updated until the function returns. |
| \value BlockWithGui Use the Qt event loop to wait for a reply. This means that the |
| user-interface will stay responsive (processing input events), |
| but it also means other events may happen, like signal delivery |
| and other D-Bus method calls. |
| \value AutoDetect Automatically detect if the called function has a reply. |
| |
| When using BlockWithGui, applications must be prepared for reentrancy in any function. |
| */ |
| |
| /*! |
| \fn void QDBusConnection::swap(QDBusConnection &other) |
| |
| Swaps this QDBusConnection instance with \a other. |
| */ |
| |
| 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 |
| |
| #endif // QT_NO_DBUS |