summaryrefslogtreecommitdiffstats
path: root/src/dbus/qdbusconnectioninterface.cpp
blob: 1b7b69933ec8f22729c7c15ae05279f5709baa95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDBus module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdbusconnectioninterface.h"

#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QMetaMethod>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtCore/QDebug>

#include "qdbus_symbols_p.h"          // for the DBUS_* constants

#ifndef QT_NO_DBUS

QT_BEGIN_NAMESPACE

/*
 * Implementation of interface class QDBusConnectionInterface
 */

/*!
    \class QDBusConnectionInterface
    \inmodule QtDBus
    \since 4.2

    \brief The QDBusConnectionInterface class provides access to the D-Bus bus daemon service.

    The D-Bus bus server daemon provides one special interface \c
    org.freedesktop.DBus that allows clients to access certain
    properties of the bus, such as the current list of clients
    connected. The QDBusConnectionInterface class provides access to that
    interface.

    The most common uses of this class are to register and unregister
    service names on the bus using the registerService() and
    unregisterService() functions, query about existing names using
    the isServiceRegistered(), registeredServiceNames() and
    serviceOwner() functions, and to receive notification that a
    client has registered or de-registered through the
    serviceRegistered(), serviceUnregistered() and serviceOwnerChanged()
    signals.
*/

/*!
    \enum QDBusConnectionInterface::ServiceQueueOptions

    Flags for determining how a service registration should behave, in
    case the service name is already registered.

    \value DontQueueService     If an application requests a name that
                                is already owned, no queueing will be
                                performed. The registeredService()
                                call will simply fail.
                                This is the default.

    \value QueueService         Attempts to register the requested
                                service, but do not try to replace it
                                if another application already has it
                                registered. Instead, simply put this
                                application in queue, until it is
                                given up. The serviceRegistered()
                                signal will be emitted when that
                                happens.

    \value ReplaceExistingService If another application already has
                                the service name registered, attempt
                                to replace it.

    \sa ServiceReplacementOptions
*/

/*!
    \enum QDBusConnectionInterface::ServiceReplacementOptions

    Flags for determining if the D-Bus server should allow another
    application to replace a name that this application has registered
    with the ReplaceExistingService option.

    The possible values are:

    \value DontAllowReplacement Do not allow another application to
                                replace us. The service must be
                                explicitly unregistered with
                                unregisterService() for another
                                application to acquire it.
                                This is the default.

    \value AllowReplacement     Allow other applications to replace us
                                with the ReplaceExistingService option
                                to registerService() without
                                intervention. If that happens, the
                                serviceUnregistered() signal will be
                                emitted.

    \sa ServiceQueueOptions
*/

/*!
    \enum QDBusConnectionInterface::RegisterServiceReply

    The possible return values from registerService():

    \value ServiceNotRegistered The call failed and the service name was not registered.
    \value ServiceRegistered    The caller is now the owner of the service name.
    \value ServiceQueued        The caller specified the QueueService flag and the
                                service was already registered, so we are in queue.

    The serviceRegistered() signal will be emitted when the service is
    acquired by this application.
*/

/*!
    \internal
*/
const char *QDBusConnectionInterface::staticInterfaceName()
{ return "org.freedesktop.DBus"; }

/*!
    \internal
*/
QDBusConnectionInterface::QDBusConnectionInterface(const QDBusConnection &connection,
                                                   QObject *parent)
    : QDBusAbstractInterface(QLatin1String(DBUS_SERVICE_DBUS),
                             QLatin1String(DBUS_PATH_DBUS),
                             DBUS_INTERFACE_DBUS, connection, parent)
{
    connect(this, SIGNAL(NameAcquired(QString)), this, SIGNAL(serviceRegistered(QString)));
    connect(this, SIGNAL(NameLost(QString)), this, SIGNAL(serviceUnregistered(QString)));
    connect(this, SIGNAL(NameOwnerChanged(QString,QString,QString)),
            this, SIGNAL(serviceOwnerChanged(QString,QString,QString)));
}

/*!
    \internal
*/
QDBusConnectionInterface::~QDBusConnectionInterface()
{
}

/*!
    Returns the unique connection name of the primary owner of the
    name \a name. If the requested name doesn't have an owner, returns
    a \c org.freedesktop.DBus.Error.NameHasNoOwner error.
*/
QDBusReply<QString> QDBusConnectionInterface::serviceOwner(const QString &name) const
{
    return internalConstCall(QDBus::AutoDetect, QLatin1String("GetNameOwner"), QList<QVariant>() << name);
}

/*!
  \property QDBusConnectionInterface::registeredServiceNames
  \brief holds the registered service names

  Lists all names currently registered on the bus.
*/
QDBusReply<QStringList> QDBusConnectionInterface::registeredServiceNames() const
{
    return internalConstCall(QDBus::AutoDetect, QLatin1String("ListNames"));
}

/*!
    Returns true if the service name \a serviceName has is currently
    registered.
*/
QDBusReply<bool> QDBusConnectionInterface::isServiceRegistered(const QString &serviceName) const
{
    return internalConstCall(QDBus::AutoDetect, QLatin1String("NameHasOwner"),
                             QList<QVariant>() << serviceName);
}

/*!
    Returns the Unix Process ID (PID) for the process currently
    holding the bus service \a serviceName.
*/
QDBusReply<uint> QDBusConnectionInterface::servicePid(const QString &serviceName) const
{
    return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixProcessID"),
                             QList<QVariant>() << serviceName);
}

/*!
    Returns the Unix User ID (UID) for the process currently holding
    the bus service \a serviceName.
*/
QDBusReply<uint> QDBusConnectionInterface::serviceUid(const QString &serviceName) const
{
    return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixUser"),
                             QList<QVariant>() << serviceName);
}

/*!
    Requests that the bus start the service given by the name \a name.
*/
QDBusReply<void> QDBusConnectionInterface::startService(const QString &name)
{
    return call(QLatin1String("StartServiceByName"), name, uint(0));
}

/*!
    Requests to register the service name \a serviceName on the
    bus. The \a qoption flag specifies how the D-Bus server should behave
    if \a serviceName is already registered. The \a roption flag
    specifies if the server should allow another application to
    replace our registered name.

    If the service registration succeeds, the serviceRegistered()
    signal will be emitted. If we are placed in queue, the signal will
    be emitted when we obtain the name. If \a roption is
    AllowReplacement, the serviceUnregistered() signal will be emitted
    if another application replaces this one.

    \sa unregisterService()
*/
QDBusReply<QDBusConnectionInterface::RegisterServiceReply>
QDBusConnectionInterface::registerService(const QString &serviceName,
                                          ServiceQueueOptions qoption,
                                          ServiceReplacementOptions roption)
{
    // reconstruct the low-level flags
    uint flags = 0;
    switch (qoption) {
    case DontQueueService:
        flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
        break;
    case QueueService:
        flags = 0;
        break;
    case ReplaceExistingService:
        flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING;
        break;
    }

    switch (roption) {
    case DontAllowReplacement:
        break;
    case AllowReplacement:
        flags |= DBUS_NAME_FLAG_ALLOW_REPLACEMENT;
        break;
    }

    QDBusMessage reply = call(QLatin1String("RequestName"), serviceName, flags);
//    qDebug() << "QDBusConnectionInterface::registerService" << serviceName << "Reply:" << reply;

    // convert the low-level flags to something that we can use
    if (reply.type() == QDBusMessage::ReplyMessage) {
        uint code = 0;

        switch (reply.arguments().at(0).toUInt()) {
        case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
        case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
            code = uint(ServiceRegistered);
            break;

        case DBUS_REQUEST_NAME_REPLY_EXISTS:
            code = uint(ServiceNotRegistered);
            break;

        case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
            code = uint(ServiceQueued);
            break;
        }

        reply.setArguments(QVariantList() << code);
    }

    return reply;
}

/*!
    Releases the claim on the bus service name \a serviceName, that
    had been previously registered with registerService(). If this
    application had ownership of the name, it will be released for
    other applications to claim. If it only had the name queued, it
    gives up its position in the queue.
*/
QDBusReply<bool>
QDBusConnectionInterface::unregisterService(const QString &serviceName)
{
    QDBusMessage reply = call(QLatin1String("ReleaseName"), serviceName);
    if (reply.type() == QDBusMessage::ReplyMessage) {
        bool success = reply.arguments().at(0).toUInt() == DBUS_RELEASE_NAME_REPLY_RELEASED;
        reply.setArguments(QVariantList() << success);
    }
    return reply;
}

/*!
    \internal
*/
void QDBusConnectionInterface::connectNotify(const QMetaMethod &signal)
{
    // translate the signal names to what we really want
    // this avoids setting hooks for signals that don't exist on the bus
    static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceRegistered);
    static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceUnregistered);
    static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceOwnerChanged);
    static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameAcquired);
    static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameLost);
    static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameOwnerChanged);
    if (signal == serviceRegisteredSignal)
        QDBusAbstractInterface::connectNotify(NameAcquiredSignal);

    else if (signal == serviceUnregisteredSignal)
        QDBusAbstractInterface::connectNotify(NameLostSignal);

    else if (signal == serviceOwnerChangedSignal) {
        static bool warningPrinted = false;
        if (!warningPrinted) {
            qWarning("Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)");
            warningPrinted = true;
        }
        QDBusAbstractInterface::connectNotify(NameOwnerChangedSignal);
    }
}

/*!
    \internal
*/
void QDBusConnectionInterface::disconnectNotify(const QMetaMethod &signal)
{
    // translate the signal names to what we really want
    // this avoids setting hooks for signals that don't exist on the bus
    static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceRegistered);
    static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceUnregistered);
    static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceOwnerChanged);
    static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameAcquired);
    static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameLost);
    static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameOwnerChanged);
    if (signal == serviceRegisteredSignal)
        QDBusAbstractInterface::disconnectNotify(NameAcquiredSignal);

    else if (signal == serviceUnregisteredSignal)
        QDBusAbstractInterface::disconnectNotify(NameLostSignal);

    else if (signal == serviceOwnerChangedSignal)
        QDBusAbstractInterface::disconnectNotify(NameOwnerChangedSignal);
}

// signals
/*!
    \fn QDBusConnectionInterface::serviceRegistered(const QString &serviceName)

    This signal is emitted by the D-Bus server when the bus service
    name (unique connection name or well-known service name) given by
    \a serviceName is acquired by this application.

    Acquisition happens after this application has requested a name using
    registerService().
*/

/*!
    \fn QDBusConnectionInterface::serviceUnregistered(const QString &serviceName)

    This signal is emitted by the D-Bus server when this application
    loses ownership of the bus service name given by \a serviceName.
*/

/*!
    \fn QDBusConnectionInterface::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)

    This signal is emitted by the D-Bus server whenever a service
    ownership change happens in the bus, including apparition and
    disparition of names.

    This signal means the application \a oldOwner lost ownership of
    bus name \a name to application \a newOwner. If \a oldOwner is an
    empty string, it means the name \a name has just been created; if
    \a newOwner is empty, the name \a name has no current owner and is
    no longer available.

    \note connecting to this signal will make the application listen for and
    receive every single service ownership change on the bus. Depending on
    how many services are running, this make the application be activated to
    receive more signals than it needs. To avoid this problem, use the
    QDBusServiceWatcher class, which can listen for specific changes.
*/

/*!
  \fn void QDBusConnectionInterface::callWithCallbackFailed(const QDBusError &error, const QDBusMessage &call)

  This signal is emitted when there is an error during a
  QDBusConnection::callWithCallback(). \a error specifies the error.
  \a call is the message that couldn't be delivered.

  \sa QDBusConnection::callWithCallback()
 */

QT_END_NAMESPACE

#endif // QT_NO_DBUS