1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qdbusconnectioninterface.h"
5
6#include <QtCore/QByteArray>
7#include <QtCore/QList>
8#include <QtCore/QMap>
9#include <QtCore/QMetaMethod>
10#include <QtCore/QString>
11#include <QtCore/QStringList>
12#include <QtCore/QVariant>
13#include <QtCore/QDebug>
14
15#include "qdbusutil_p.h" // for the DBUS_* constants
16
17#ifndef QT_NO_DBUS
18
19QT_BEGIN_NAMESPACE
20
21using namespace Qt::StringLiterals;
22
23/*
24 * Implementation of interface class QDBusConnectionInterface
25 */
26
27/*!
28 \class QDBusConnectionInterface
29 \inmodule QtDBus
30 \since 4.2
31
32 \brief The QDBusConnectionInterface class provides access to the D-Bus bus daemon service.
33
34 The D-Bus bus server daemon provides one special interface \c
35 org.freedesktop.DBus that allows clients to access certain
36 properties of the bus, such as the current list of clients
37 connected. The QDBusConnectionInterface class provides access to that
38 interface.
39
40 The most common uses of this class are to register and unregister
41 service names on the bus using the registerService() and
42 unregisterService() functions, query about existing names using
43 the isServiceRegistered(), registeredServiceNames() and
44 serviceOwner() functions, and to receive notification that a
45 client has registered or de-registered through the
46 serviceRegistered(), serviceUnregistered() and serviceOwnerChanged()
47 signals.
48*/
49
50/*!
51 \enum QDBusConnectionInterface::ServiceQueueOptions
52
53 Flags for determining how a service registration should behave, in
54 case the service name is already registered.
55
56 \value DontQueueService If an application requests a name that
57 is already owned, no queueing will be
58 performed. The registeredService()
59 call will simply fail.
60 This is the default.
61
62 \value QueueService Attempts to register the requested
63 service, but do not try to replace it
64 if another application already has it
65 registered. Instead, simply put this
66 application in queue, until it is
67 given up. The serviceRegistered()
68 signal will be emitted when that
69 happens.
70
71 \value ReplaceExistingService If another application already has
72 the service name registered, attempt
73 to replace it.
74
75 \sa ServiceReplacementOptions
76*/
77
78/*!
79 \enum QDBusConnectionInterface::ServiceReplacementOptions
80
81 Flags for determining if the D-Bus server should allow another
82 application to replace a name that this application has registered
83 with the ReplaceExistingService option.
84
85 The possible values are:
86
87 \value DontAllowReplacement Do not allow another application to
88 replace us. The service must be
89 explicitly unregistered with
90 unregisterService() for another
91 application to acquire it.
92 This is the default.
93
94 \value AllowReplacement Allow other applications to replace us
95 with the ReplaceExistingService option
96 to registerService() without
97 intervention. If that happens, the
98 serviceUnregistered() signal will be
99 emitted.
100
101 \sa ServiceQueueOptions
102*/
103
104/*!
105 \enum QDBusConnectionInterface::RegisterServiceReply
106
107 The possible return values from registerService():
108
109 \value ServiceNotRegistered The call failed and the service name was not registered.
110 \value ServiceRegistered The caller is now the owner of the service name.
111 \value ServiceQueued The caller specified the QueueService flag and the
112 service was already registered, so we are in queue.
113
114 The serviceRegistered() signal will be emitted when the service is
115 acquired by this application.
116*/
117
118/*!
119 \internal
120*/
121const char *QDBusConnectionInterface::staticInterfaceName()
122{ return "org.freedesktop.DBus"; }
123
124/*!
125 \internal
126*/
127QDBusConnectionInterface::QDBusConnectionInterface(const QDBusConnection &connection,
128 QObject *parent)
129 : QDBusAbstractInterface(QDBusUtil::dbusService(),
130 QDBusUtil::dbusPath(),
131 DBUS_INTERFACE_DBUS, connection, parent)
132{
133 connect(sender: this, signal: &QDBusConnectionInterface::NameAcquired, context: this, emit slot: &QDBusConnectionInterface::serviceRegistered);
134 connect(sender: this, signal: &QDBusConnectionInterface::NameLost, context: this, emit slot: &QDBusConnectionInterface::serviceUnregistered);
135 connect(sender: this, signal: &QDBusConnectionInterface::NameOwnerChanged,
136 context: this, emit slot: &QDBusConnectionInterface::serviceOwnerChanged);
137}
138
139/*!
140 \internal
141*/
142QDBusConnectionInterface::~QDBusConnectionInterface()
143{
144}
145
146/*!
147 Returns the unique connection name of the primary owner of the
148 name \a name. If the requested name doesn't have an owner, returns
149 a \c org.freedesktop.DBus.Error.NameHasNoOwner error.
150*/
151QDBusReply<QString> QDBusConnectionInterface::serviceOwner(const QString &name) const
152{
153 return internalConstCall(mode: QDBus::AutoDetect, method: "GetNameOwner"_L1, args: QList<QVariant>() << name);
154}
155
156/*!
157 \property QDBusConnectionInterface::registeredServiceNames
158 \brief holds the registered service names
159
160 Lists all names currently registered on the bus.
161*/
162QDBusReply<QStringList> QDBusConnectionInterface::registeredServiceNames() const
163{
164 return internalConstCall(mode: QDBus::AutoDetect, method: "ListNames"_L1);
165}
166
167/*!
168 \property QDBusConnectionInterface::activatableServiceNames
169 \brief holds the activatable service names
170 \since 5.14
171
172 Lists all names that can be activated on the bus.
173*/
174QDBusReply<QStringList> QDBusConnectionInterface::activatableServiceNames() const
175{
176 return internalConstCall(mode: QDBus::AutoDetect, method: "ListActivatableNames"_L1);
177}
178
179/*!
180 Returns \c true if the service name \a serviceName has is currently
181 registered.
182*/
183QDBusReply<bool> QDBusConnectionInterface::isServiceRegistered(const QString &serviceName) const
184{
185 return internalConstCall(mode: QDBus::AutoDetect, method: "NameHasOwner"_L1,
186 args: QList<QVariant>() << serviceName);
187}
188
189/*!
190 Returns the Unix Process ID (PID) for the process currently
191 holding the bus service \a serviceName.
192*/
193QDBusReply<uint> QDBusConnectionInterface::servicePid(const QString &serviceName) const
194{
195 return internalConstCall(mode: QDBus::AutoDetect, method: "GetConnectionUnixProcessID"_L1,
196 args: QList<QVariant>() << serviceName);
197}
198
199/*!
200 Returns the Unix User ID (UID) for the process currently holding
201 the bus service \a serviceName.
202*/
203QDBusReply<uint> QDBusConnectionInterface::serviceUid(const QString &serviceName) const
204{
205 return internalConstCall(mode: QDBus::AutoDetect, method: "GetConnectionUnixUser"_L1,
206 args: QList<QVariant>() << serviceName);
207}
208
209/*!
210 Requests that the bus start the service given by the name \a name.
211*/
212QDBusReply<void> QDBusConnectionInterface::startService(const QString &name)
213{
214 return call(method: "StartServiceByName"_L1, args: name, args: uint(0));
215}
216
217/*!
218 Requests to register the service name \a serviceName on the
219 bus. The \a qoption flag specifies how the D-Bus server should behave
220 if \a serviceName is already registered. The \a roption flag
221 specifies if the server should allow another application to
222 replace our registered name.
223
224 If the service registration succeeds, the serviceRegistered()
225 signal will be emitted. If we are placed in queue, the signal will
226 be emitted when we obtain the name. If \a roption is
227 AllowReplacement, the serviceUnregistered() signal will be emitted
228 if another application replaces this one.
229
230 \sa unregisterService()
231*/
232QDBusReply<QDBusConnectionInterface::RegisterServiceReply>
233QDBusConnectionInterface::registerService(const QString &serviceName,
234 ServiceQueueOptions qoption,
235 ServiceReplacementOptions roption)
236{
237 // reconstruct the low-level flags
238 uint flags = 0;
239 switch (qoption) {
240 case DontQueueService:
241 flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
242 break;
243 case QueueService:
244 flags = 0;
245 break;
246 case ReplaceExistingService:
247 flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING;
248 break;
249 }
250
251 switch (roption) {
252 case DontAllowReplacement:
253 break;
254 case AllowReplacement:
255 flags |= DBUS_NAME_FLAG_ALLOW_REPLACEMENT;
256 break;
257 }
258
259 QDBusMessage reply = call(method: "RequestName"_L1, args: serviceName, args&: flags);
260// qDebug() << "QDBusConnectionInterface::registerService" << serviceName << "Reply:" << reply;
261
262 // convert the low-level flags to something that we can use
263 if (reply.type() == QDBusMessage::ReplyMessage) {
264 uint code = 0;
265
266 switch (reply.arguments().at(i: 0).toUInt()) {
267 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
268 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
269 code = uint(ServiceRegistered);
270 break;
271
272 case DBUS_REQUEST_NAME_REPLY_EXISTS:
273 code = uint(ServiceNotRegistered);
274 break;
275
276 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
277 code = uint(ServiceQueued);
278 break;
279 }
280
281 reply.setArguments(QVariantList() << code);
282 }
283
284 return reply;
285}
286
287/*!
288 Releases the claim on the bus service name \a serviceName, that
289 had been previously registered with registerService(). If this
290 application had ownership of the name, it will be released for
291 other applications to claim. If it only had the name queued, it
292 gives up its position in the queue.
293*/
294QDBusReply<bool>
295QDBusConnectionInterface::unregisterService(const QString &serviceName)
296{
297 QDBusMessage reply = call(method: "ReleaseName"_L1, args: serviceName);
298 if (reply.type() == QDBusMessage::ReplyMessage) {
299 bool success = reply.arguments().at(i: 0).toUInt() == DBUS_RELEASE_NAME_REPLY_RELEASED;
300 reply.setArguments(QVariantList() << success);
301 }
302 return reply;
303}
304
305/*!
306 \internal
307*/
308void QDBusConnectionInterface::connectNotify(const QMetaMethod &signal)
309{
310 // translate the signal names to what we really want
311 // this avoids setting hooks for signals that don't exist on the bus
312 static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceRegistered);
313 static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceUnregistered);
314 static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceOwnerChanged);
315 static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameAcquired);
316 static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameLost);
317 static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameOwnerChanged);
318 if (signal == serviceRegisteredSignal)
319 QDBusAbstractInterface::connectNotify(signal: NameAcquiredSignal);
320
321 else if (signal == serviceUnregisteredSignal)
322 QDBusAbstractInterface::connectNotify(signal: NameLostSignal);
323
324 else if (signal == serviceOwnerChangedSignal) {
325 static bool warningPrinted = false;
326 if (!warningPrinted) {
327 qWarning(msg: "Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)");
328 warningPrinted = true;
329 }
330 QDBusAbstractInterface::connectNotify(signal: NameOwnerChangedSignal);
331 }
332}
333
334/*!
335 \internal
336*/
337void QDBusConnectionInterface::disconnectNotify(const QMetaMethod &signal)
338{
339 // translate the signal names to what we really want
340 // this avoids setting hooks for signals that don't exist on the bus
341 static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceRegistered);
342 static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceUnregistered);
343 static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::serviceOwnerChanged);
344 static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameAcquired);
345 static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameLost);
346 static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(signal: &QDBusConnectionInterface::NameOwnerChanged);
347 if (signal == serviceRegisteredSignal)
348 QDBusAbstractInterface::disconnectNotify(signal: NameAcquiredSignal);
349
350 else if (signal == serviceUnregisteredSignal)
351 QDBusAbstractInterface::disconnectNotify(signal: NameLostSignal);
352
353 else if (signal == serviceOwnerChangedSignal)
354 QDBusAbstractInterface::disconnectNotify(signal: NameOwnerChangedSignal);
355}
356
357// signals
358/*!
359 \fn QDBusConnectionInterface::serviceRegistered(const QString &service)
360
361 This signal is emitted by the D-Bus server when the bus service
362 name (unique connection name or well-known service name) given by
363 \a service is acquired by this application.
364
365 Acquisition happens after this application has requested a name using
366 registerService().
367*/
368
369/*!
370 \fn QDBusConnectionInterface::serviceUnregistered(const QString &service)
371
372 This signal is emitted by the D-Bus server when this application
373 loses ownership of the bus service name given by \a service.
374*/
375
376/*!
377 \fn QDBusConnectionInterface::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
378 \deprecated
379
380 Use QDBusServiceWatcher instead.
381
382 This signal is emitted by the D-Bus server whenever a service
383 ownership change happens in the bus, including apparition and
384 disparition of names.
385
386 This signal means the application \a oldOwner lost ownership of
387 bus name \a name to application \a newOwner. If \a oldOwner is an
388 empty string, it means the name \a name has just been created; if
389 \a newOwner is empty, the name \a name has no current owner and is
390 no longer available.
391
392 \note connecting to this signal will make the application listen for and
393 receive every single service ownership change on the bus. Depending on
394 how many services are running, this make the application be activated to
395 receive more signals than it needs. To avoid this problem, use the
396 QDBusServiceWatcher class, which can listen for specific changes.
397*/
398
399/*!
400 \fn void QDBusConnectionInterface::callWithCallbackFailed(const QDBusError &error, const QDBusMessage &call)
401
402 This signal is emitted when there is an error during a
403 QDBusConnection::callWithCallback(). \a error specifies the error.
404 \a call is the message that couldn't be delivered.
405
406 \sa QDBusConnection::callWithCallback()
407 */
408
409QT_END_NAMESPACE
410
411#include "moc_qdbusconnectioninterface.cpp"
412
413#endif // QT_NO_DBUS
414

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