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