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