|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDBus module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <qcoreapplication.h> |
|
43 #include <qdebug.h> |
|
44 #include <qmetaobject.h> |
|
45 #include <qobject.h> |
|
46 #include <qsocketnotifier.h> |
|
47 #include <qstringlist.h> |
|
48 #include <qtimer.h> |
|
49 #include <qthread.h> |
|
50 |
|
51 #include "qdbusargument.h" |
|
52 #include "qdbusconnection_p.h" |
|
53 #include "qdbusinterface_p.h" |
|
54 #include "qdbusmessage.h" |
|
55 #include "qdbusmetatype.h" |
|
56 #include "qdbusmetatype_p.h" |
|
57 #include "qdbusabstractadaptor.h" |
|
58 #include "qdbusabstractadaptor_p.h" |
|
59 #include "qdbusutil_p.h" |
|
60 #include "qdbusmessage_p.h" |
|
61 #include "qdbuscontext_p.h" |
|
62 #include "qdbuspendingcall_p.h" |
|
63 #include "qdbusintegrator_p.h" |
|
64 |
|
65 #include "qdbusthreaddebug_p.h" |
|
66 |
|
67 QT_BEGIN_NAMESPACE |
|
68 |
|
69 static bool isDebugging; |
|
70 #define qDBusDebug if (!::isDebugging); else qDebug |
|
71 |
|
72 static inline QDebug operator<<(QDebug dbg, const QThread *th) |
|
73 { |
|
74 dbg.nospace() << "QThread(ptr=" << (void*)th; |
|
75 if (th && !th->objectName().isEmpty()) |
|
76 dbg.nospace() << ", name=" << th->objectName(); |
|
77 dbg.nospace() << ')'; |
|
78 return dbg.space(); |
|
79 } |
|
80 |
|
81 #if QDBUS_THREAD_DEBUG |
|
82 static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn) |
|
83 { |
|
84 dbg.nospace() << "QDBusConnection(" |
|
85 << "ptr=" << (void*)conn |
|
86 << ", name=" << conn->name |
|
87 << ", baseService=" << conn->baseService |
|
88 << ", thread="; |
|
89 if (conn->thread() == QThread::currentThread()) |
|
90 dbg.nospace() << "same thread"; |
|
91 else |
|
92 dbg.nospace() << conn->thread(); |
|
93 dbg.nospace() << ')'; |
|
94 return dbg.space(); |
|
95 } |
|
96 |
|
97 Q_AUTOTEST_EXPORT void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn) |
|
98 { |
|
99 qDBusDebug() << QThread::currentThread() |
|
100 << "QtDBus threading action" << action |
|
101 << (condition == QDBusLockerBase::BeforeLock ? "before lock" : |
|
102 condition == QDBusLockerBase::AfterLock ? "after lock" : |
|
103 condition == QDBusLockerBase::BeforeUnlock ? "before unlock" : |
|
104 condition == QDBusLockerBase::AfterUnlock ? "after unlock" : |
|
105 condition == QDBusLockerBase::BeforePost ? "before event posting" : |
|
106 condition == QDBusLockerBase::AfterPost ? "after event posting" : |
|
107 condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" : |
|
108 condition == QDBusLockerBase::AfterDeliver ? "after event delivery" : |
|
109 condition == QDBusLockerBase::BeforeAcquire ? "before acquire" : |
|
110 condition == QDBusLockerBase::AfterAcquire ? "after acquire" : |
|
111 condition == QDBusLockerBase::BeforeRelease ? "before release" : |
|
112 condition == QDBusLockerBase::AfterRelease ? "after release" : |
|
113 "condition unknown") |
|
114 << "in connection" << conn; |
|
115 } |
|
116 Q_AUTOTEST_EXPORT qdbusThreadDebugFunc qdbusThreadDebug = 0; |
|
117 #endif |
|
118 |
|
119 typedef void (*QDBusSpyHook)(const QDBusMessage&); |
|
120 typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList; |
|
121 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) |
|
122 |
|
123 extern "C" { |
|
124 |
|
125 // libdbus-1 callbacks |
|
126 |
|
127 static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms); |
|
128 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) |
|
129 { |
|
130 Q_ASSERT(timeout); |
|
131 Q_ASSERT(data); |
|
132 |
|
133 // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout)); |
|
134 |
|
135 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
136 |
|
137 if (!q_dbus_timeout_get_enabled(timeout)) |
|
138 return true; |
|
139 |
|
140 QDBusWatchAndTimeoutLocker locker(AddTimeoutAction, d); |
|
141 if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { |
|
142 // correct thread |
|
143 return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout)); |
|
144 } else { |
|
145 // wrong thread: sync back |
|
146 QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; |
|
147 ev->subtype = QDBusConnectionCallbackEvent::AddTimeout; |
|
148 d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout))); |
|
149 d->postEventToThread(AddTimeoutAction, d, ev); |
|
150 return true; |
|
151 } |
|
152 } |
|
153 |
|
154 static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms) |
|
155 { |
|
156 Q_ASSERT(d->timeouts.keys(timeout).isEmpty()); |
|
157 |
|
158 int timerId = d->startTimer(ms); |
|
159 if (!timerId) |
|
160 return false; |
|
161 |
|
162 d->timeouts[timerId] = timeout; |
|
163 return true; |
|
164 } |
|
165 |
|
166 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) |
|
167 { |
|
168 Q_ASSERT(timeout); |
|
169 Q_ASSERT(data); |
|
170 |
|
171 // qDebug("removeTimeout"); |
|
172 |
|
173 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
174 |
|
175 QDBusWatchAndTimeoutLocker locker(RemoveTimeoutAction, d); |
|
176 |
|
177 // is it pending addition? |
|
178 QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin(); |
|
179 while (pit != d->timeoutsPendingAdd.end()) { |
|
180 if (pit->first == timeout) |
|
181 pit = d->timeoutsPendingAdd.erase(pit); |
|
182 else |
|
183 ++pit; |
|
184 } |
|
185 |
|
186 // is it a running timer? |
|
187 bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread(); |
|
188 QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); |
|
189 while (it != d->timeouts.end()) { |
|
190 if (it.value() == timeout) { |
|
191 if (correctThread) { |
|
192 // correct thread |
|
193 d->killTimer(it.key()); |
|
194 } else { |
|
195 // incorrect thread or no application, post an event for later |
|
196 QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; |
|
197 ev->subtype = QDBusConnectionCallbackEvent::KillTimer; |
|
198 ev->timerId = it.key(); |
|
199 d->postEventToThread(KillTimerAction, d, ev); |
|
200 } |
|
201 it = d->timeouts.erase(it); |
|
202 break; |
|
203 } else { |
|
204 ++it; |
|
205 } |
|
206 } |
|
207 } |
|
208 |
|
209 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) |
|
210 { |
|
211 Q_ASSERT(timeout); |
|
212 Q_ASSERT(data); |
|
213 |
|
214 //qDebug("ToggleTimeout"); |
|
215 |
|
216 qDBusRemoveTimeout(timeout, data); |
|
217 qDBusAddTimeout(timeout, data); |
|
218 } |
|
219 |
|
220 static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd); |
|
221 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) |
|
222 { |
|
223 Q_ASSERT(watch); |
|
224 Q_ASSERT(data); |
|
225 |
|
226 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
227 |
|
228 int flags = q_dbus_watch_get_flags(watch); |
|
229 int fd = q_dbus_watch_get_fd(watch); |
|
230 |
|
231 if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { |
|
232 return qDBusRealAddWatch(d, watch, flags, fd); |
|
233 } else { |
|
234 QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; |
|
235 ev->subtype = QDBusConnectionCallbackEvent::AddWatch; |
|
236 ev->watch = watch; |
|
237 ev->fd = fd; |
|
238 ev->extra = flags; |
|
239 d->postEventToThread(AddWatchAction, d, ev); |
|
240 return true; |
|
241 } |
|
242 } |
|
243 |
|
244 static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd) |
|
245 { |
|
246 QDBusConnectionPrivate::Watcher watcher; |
|
247 |
|
248 QDBusWatchAndTimeoutLocker locker(AddWatchAction, d); |
|
249 if (flags & DBUS_WATCH_READABLE) { |
|
250 //qDebug("addReadWatch %d", fd); |
|
251 watcher.watch = watch; |
|
252 if (QCoreApplication::instance()) { |
|
253 watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); |
|
254 watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); |
|
255 d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); |
|
256 } |
|
257 } |
|
258 if (flags & DBUS_WATCH_WRITABLE) { |
|
259 //qDebug("addWriteWatch %d", fd); |
|
260 watcher.watch = watch; |
|
261 if (QCoreApplication::instance()) { |
|
262 watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); |
|
263 watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); |
|
264 d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); |
|
265 } |
|
266 } |
|
267 d->watchers.insertMulti(fd, watcher); |
|
268 |
|
269 return true; |
|
270 } |
|
271 |
|
272 static void qDBusRemoveWatch(DBusWatch *watch, void *data) |
|
273 { |
|
274 Q_ASSERT(watch); |
|
275 Q_ASSERT(data); |
|
276 |
|
277 //qDebug("remove watch"); |
|
278 |
|
279 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
280 int fd = q_dbus_watch_get_fd(watch); |
|
281 |
|
282 QDBusWatchAndTimeoutLocker locker(RemoveWatchAction, d); |
|
283 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); |
|
284 while (i != d->watchers.end() && i.key() == fd) { |
|
285 if (i.value().watch == watch) { |
|
286 if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { |
|
287 // correct thread, delete the socket notifiers |
|
288 delete i.value().read; |
|
289 delete i.value().write; |
|
290 } else { |
|
291 // incorrect thread or no application, use delete later |
|
292 if (i->read) |
|
293 i->read->deleteLater(); |
|
294 if (i->write) |
|
295 i->write->deleteLater(); |
|
296 } |
|
297 i = d->watchers.erase(i); |
|
298 } else { |
|
299 ++i; |
|
300 } |
|
301 } |
|
302 } |
|
303 |
|
304 static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd); |
|
305 static void qDBusToggleWatch(DBusWatch *watch, void *data) |
|
306 { |
|
307 Q_ASSERT(watch); |
|
308 Q_ASSERT(data); |
|
309 |
|
310 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
311 int fd = q_dbus_watch_get_fd(watch); |
|
312 |
|
313 if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { |
|
314 qDBusRealToggleWatch(d, watch, fd); |
|
315 } else { |
|
316 QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; |
|
317 ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch; |
|
318 ev->watch = watch; |
|
319 ev->fd = fd; |
|
320 d->postEventToThread(ToggleWatchAction, d, ev); |
|
321 } |
|
322 } |
|
323 |
|
324 static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd) |
|
325 { |
|
326 QDBusWatchAndTimeoutLocker locker(ToggleWatchAction, d); |
|
327 |
|
328 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); |
|
329 while (i != d->watchers.end() && i.key() == fd) { |
|
330 if (i.value().watch == watch) { |
|
331 bool enabled = q_dbus_watch_get_enabled(watch); |
|
332 int flags = q_dbus_watch_get_flags(watch); |
|
333 |
|
334 //qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE); |
|
335 |
|
336 if (flags & DBUS_WATCH_READABLE && i.value().read) |
|
337 i.value().read->setEnabled(enabled); |
|
338 if (flags & DBUS_WATCH_WRITABLE && i.value().write) |
|
339 i.value().write->setEnabled(enabled); |
|
340 return; |
|
341 } |
|
342 ++i; |
|
343 } |
|
344 } |
|
345 |
|
346 static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) |
|
347 { |
|
348 Q_ASSERT(connection); |
|
349 Q_UNUSED(connection); |
|
350 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
351 |
|
352 static int slotId; // 0 is QObject::deleteLater() |
|
353 if (!slotId) { |
|
354 // it's ok to do this: there's no race condition because the store is atomic |
|
355 // and we always set to the same value |
|
356 slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()"); |
|
357 } |
|
358 |
|
359 //qDBusDebug() << "Updating dispatcher status" << slotId; |
|
360 if (new_status == DBUS_DISPATCH_DATA_REMAINS) |
|
361 QDBusConnectionPrivate::staticMetaObject.method(slotId). |
|
362 invoke(d, Qt::QueuedConnection); |
|
363 } |
|
364 |
|
365 static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data) |
|
366 { |
|
367 // ### We may want to separate the server from the QDBusConnectionPrivate |
|
368 Q_ASSERT(server); Q_UNUSED(server); |
|
369 Q_ASSERT(connection); |
|
370 Q_ASSERT(data); |
|
371 |
|
372 // keep the connection alive |
|
373 q_dbus_connection_ref(connection); |
|
374 QDBusConnectionPrivate *d = new QDBusConnectionPrivate; |
|
375 |
|
376 // setConnection does the error handling for us |
|
377 QDBusErrorInternal error; |
|
378 d->setPeer(connection, error); |
|
379 |
|
380 QDBusConnection retval = QDBusConnectionPrivate::q(d); |
|
381 d->setBusService(retval); |
|
382 |
|
383 //d->name = QString::number(reinterpret_cast<int>(d)); |
|
384 //d->setConnection(d->name, d); |
|
385 |
|
386 // make QDBusServer emit the newConnection signal |
|
387 QDBusConnectionPrivate *server_d = static_cast<QDBusConnectionPrivate *>(data); |
|
388 server_d->serverConnection(retval); |
|
389 } |
|
390 |
|
391 } // extern "C" |
|
392 |
|
393 static QByteArray buildMatchRule(const QString &service, const QString & /*owner*/, |
|
394 const QString &objectPath, const QString &interface, |
|
395 const QString &member, const QString & /*signature*/) |
|
396 { |
|
397 QString result = QLatin1String("type='signal',"); |
|
398 QString keyValue = QLatin1String("%1='%2',"); |
|
399 |
|
400 if (!service.isEmpty()) |
|
401 result += keyValue.arg(QLatin1String("sender"), service); |
|
402 if (!objectPath.isEmpty()) |
|
403 result += keyValue.arg(QLatin1String("path"), objectPath); |
|
404 if (!interface.isEmpty()) |
|
405 result += keyValue.arg(QLatin1String("interface"), interface); |
|
406 if (!member.isEmpty()) |
|
407 result += keyValue.arg(QLatin1String("member"), member); |
|
408 |
|
409 result.chop(1); // remove ending comma |
|
410 return result.toLatin1(); |
|
411 } |
|
412 |
|
413 static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, |
|
414 const QString &fullpath, int &usedLength, |
|
415 QDBusConnectionPrivate::ObjectTreeNode &result) |
|
416 { |
|
417 int start = 0; |
|
418 int length = fullpath.length(); |
|
419 if (fullpath.at(0) == QLatin1Char('/')) |
|
420 start = 1; |
|
421 |
|
422 // walk the object tree |
|
423 const QDBusConnectionPrivate::ObjectTreeNode *node = root; |
|
424 while (start < length && node && !(node->flags & QDBusConnection::ExportChildObjects)) { |
|
425 int end = fullpath.indexOf(QLatin1Char('/'), start); |
|
426 end = (end == -1 ? length : end); |
|
427 QStringRef pathComponent(&fullpath, start, end - start); |
|
428 |
|
429 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = |
|
430 qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponent); |
|
431 if (it != node->children.constEnd() && it->name == pathComponent) |
|
432 // match |
|
433 node = it; |
|
434 else |
|
435 node = 0; |
|
436 |
|
437 start = end + 1; |
|
438 } |
|
439 |
|
440 // found our object |
|
441 usedLength = (start > length ? length : start); |
|
442 if (node) { |
|
443 if (node->obj || !node->children.isEmpty()) |
|
444 result = *node; |
|
445 else |
|
446 // there really is no object here |
|
447 // we're just looking at an unused space in the QVector |
|
448 node = 0; |
|
449 } |
|
450 return node; |
|
451 } |
|
452 |
|
453 static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, |
|
454 const QString &fullpath, int start) |
|
455 { |
|
456 int length = fullpath.length(); |
|
457 |
|
458 // any object in the tree can tell us to switch to its own object tree: |
|
459 const QDBusConnectionPrivate::ObjectTreeNode *node = root; |
|
460 if (node && node->flags & QDBusConnection::ExportChildObjects) { |
|
461 QObject *obj = node->obj; |
|
462 |
|
463 while (obj) { |
|
464 if (start >= length) |
|
465 // we're at the correct level |
|
466 return obj; |
|
467 |
|
468 int pos = fullpath.indexOf(QLatin1Char('/'), start); |
|
469 pos = (pos == -1 ? length : pos); |
|
470 QStringRef pathComponent(&fullpath, start, pos - start); |
|
471 |
|
472 const QObjectList children = obj->children(); |
|
473 |
|
474 // find a child with the proper name |
|
475 QObject *next = 0; |
|
476 QObjectList::ConstIterator it = children.constBegin(); |
|
477 QObjectList::ConstIterator end = children.constEnd(); |
|
478 for ( ; it != end; ++it) |
|
479 if ((*it)->objectName() == pathComponent) { |
|
480 next = *it; |
|
481 break; |
|
482 } |
|
483 |
|
484 if (!next) |
|
485 break; |
|
486 |
|
487 obj = next; |
|
488 start = pos + 1; |
|
489 } |
|
490 } |
|
491 |
|
492 // object not found |
|
493 return 0; |
|
494 } |
|
495 |
|
496 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); |
|
497 void qDBusAddSpyHook(QDBusSpyHook hook) |
|
498 { |
|
499 qDBusSpyHookList()->append(hook); |
|
500 } |
|
501 |
|
502 extern "C" { |
|
503 static DBusHandlerResult |
|
504 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) |
|
505 { |
|
506 Q_ASSERT(data); |
|
507 Q_UNUSED(connection); |
|
508 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); |
|
509 if (d->mode == QDBusConnectionPrivate::InvalidMode) |
|
510 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
511 |
|
512 QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message); |
|
513 qDBusDebug() << QThread::currentThread() << "got message:" << amsg; |
|
514 |
|
515 return d->handleMessage(amsg) ? |
|
516 DBUS_HANDLER_RESULT_HANDLED : |
|
517 DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
518 } |
|
519 } |
|
520 |
|
521 bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) |
|
522 { |
|
523 const QDBusSpyHookList *list = qDBusSpyHookList(); |
|
524 for (int i = 0; i < list->size(); ++i) { |
|
525 qDBusDebug() << "calling the message spy hook"; |
|
526 (*(*list)[i])(amsg); |
|
527 } |
|
528 |
|
529 switch (amsg.type()) { |
|
530 case QDBusMessage::SignalMessage: |
|
531 handleSignal(amsg); |
|
532 return true; |
|
533 break; |
|
534 case QDBusMessage::MethodCallMessage: |
|
535 handleObjectCall(amsg); |
|
536 return true; |
|
537 case QDBusMessage::ReplyMessage: |
|
538 case QDBusMessage::ErrorMessage: |
|
539 return false; // we don't handle those here |
|
540 case QDBusMessage::InvalidMessage: |
|
541 Q_ASSERT_X(false, "QDBusConnection", "Invalid message found when processing"); |
|
542 break; |
|
543 } |
|
544 |
|
545 return false; |
|
546 } |
|
547 |
|
548 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack) |
|
549 { |
|
550 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin(); |
|
551 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = haystack.children.end(); |
|
552 for ( ; it != end; ++it) |
|
553 huntAndDestroy(needle, *it); |
|
554 |
|
555 if (needle == haystack.obj) { |
|
556 haystack.obj = 0; |
|
557 haystack.flags = 0; |
|
558 } |
|
559 } |
|
560 |
|
561 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, |
|
562 QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, |
|
563 bool isScriptable, bool isAdaptor, const QString &path = QString()) |
|
564 { |
|
565 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin(); |
|
566 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd(); |
|
567 for ( ; it != end; ++it) |
|
568 huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name); |
|
569 |
|
570 if (needle == haystack.obj) { |
|
571 // is this a signal we should relay? |
|
572 if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0) |
|
573 return; // no: it comes from an adaptor and we're not exporting adaptors |
|
574 else if (!isAdaptor) { |
|
575 int mask = isScriptable |
|
576 ? QDBusConnection::ExportScriptableSignals |
|
577 : QDBusConnection::ExportNonScriptableSignals; |
|
578 if ((haystack.flags & mask) == 0) |
|
579 return; // signal was not exported |
|
580 } |
|
581 |
|
582 QByteArray p = path.toLatin1(); |
|
583 if (p.isEmpty()) |
|
584 p = "/"; |
|
585 qDBusDebug() << QThread::currentThread() << "emitting signal at" << p; |
|
586 DBusMessage *msg2 = q_dbus_message_copy(msg); |
|
587 q_dbus_message_set_path(msg2, p); |
|
588 q_dbus_connection_send(connection, msg2, 0); |
|
589 q_dbus_message_unref(msg2); |
|
590 } |
|
591 } |
|
592 |
|
593 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, |
|
594 const QString &signature_, QList<int>& metaTypes) |
|
595 { |
|
596 QByteArray msgSignature = signature_.toLatin1(); |
|
597 |
|
598 for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { |
|
599 QMetaMethod mm = mo->method(idx); |
|
600 |
|
601 // check access: |
|
602 if (mm.access() != QMetaMethod::Public) |
|
603 continue; |
|
604 |
|
605 // check type: |
|
606 if (mm.methodType() != QMetaMethod::Slot) |
|
607 continue; |
|
608 |
|
609 // check name: |
|
610 QByteArray slotname = mm.signature(); |
|
611 int paren = slotname.indexOf('('); |
|
612 if (paren != name.length() || !slotname.startsWith(name)) |
|
613 continue; |
|
614 |
|
615 int returnType = qDBusNameToTypeId(mm.typeName()); |
|
616 bool isAsync = qDBusCheckAsyncTag(mm.tag()); |
|
617 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; |
|
618 |
|
619 // consistency check: |
|
620 if (isAsync && returnType != QMetaType::Void) |
|
621 continue; |
|
622 |
|
623 int inputCount = qDBusParametersForMethod(mm, metaTypes); |
|
624 if (inputCount == -1) |
|
625 continue; // problem parsing |
|
626 |
|
627 metaTypes[0] = returnType; |
|
628 bool hasMessage = false; |
|
629 if (inputCount > 0 && |
|
630 metaTypes.at(inputCount) == QDBusMetaTypeId::message) { |
|
631 // "no input parameters" is allowed as long as the message meta type is there |
|
632 hasMessage = true; |
|
633 --inputCount; |
|
634 } |
|
635 |
|
636 // try to match the parameters |
|
637 int i; |
|
638 QByteArray reconstructedSignature; |
|
639 for (i = 1; i <= inputCount; ++i) { |
|
640 const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) ); |
|
641 if (!typeSignature) |
|
642 break; // invalid |
|
643 |
|
644 reconstructedSignature += typeSignature; |
|
645 if (!msgSignature.startsWith(reconstructedSignature)) |
|
646 break; |
|
647 } |
|
648 |
|
649 if (reconstructedSignature != msgSignature) |
|
650 continue; // we didn't match them all |
|
651 |
|
652 if (hasMessage) |
|
653 ++i; |
|
654 |
|
655 // make sure that the output parameters have signatures too |
|
656 if (returnType != 0 && QDBusMetaType::typeToSignature(returnType) == 0) |
|
657 continue; |
|
658 |
|
659 bool ok = true; |
|
660 for (int j = i; ok && j < metaTypes.count(); ++j) |
|
661 if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0) |
|
662 ok = false; |
|
663 if (!ok) |
|
664 continue; |
|
665 |
|
666 // consistency check: |
|
667 if (isAsync && metaTypes.count() > i + 1) |
|
668 continue; |
|
669 |
|
670 if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0) |
|
671 continue; // not exported |
|
672 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0) |
|
673 continue; // not exported |
|
674 |
|
675 // if we got here, this slot matched |
|
676 return idx; |
|
677 } |
|
678 |
|
679 // no slot matched |
|
680 return -1; |
|
681 } |
|
682 |
|
683 QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target, |
|
684 QObject *object, int idx, |
|
685 const QList<int> &metaTypes, |
|
686 const QDBusMessage &msg) |
|
687 { |
|
688 Q_ASSERT(object); |
|
689 Q_UNUSED(object); |
|
690 |
|
691 int n = metaTypes.count() - 1; |
|
692 if (metaTypes[n] == QDBusMetaTypeId::message) |
|
693 --n; |
|
694 |
|
695 // check that types match |
|
696 for (int i = 0; i < n; ++i) |
|
697 if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() && |
|
698 msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>()) |
|
699 return 0; // no match |
|
700 |
|
701 // we can deliver |
|
702 // prepare for the call |
|
703 return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes); |
|
704 } |
|
705 |
|
706 void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook, |
|
707 const QDBusMessage &msg) |
|
708 { |
|
709 // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal |
|
710 // that was received from D-Bus |
|
711 // |
|
712 // Signals are delivered to slots if the parameters match |
|
713 // Slots can have less parameters than there are on the message |
|
714 // Slots can optionally have one final parameter that is a QDBusMessage |
|
715 // Slots receive read-only copies of the message (i.e., pass by value or by const-ref) |
|
716 QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg); |
|
717 if (call) |
|
718 postEventToThread(ActivateSignalAction, hook.obj, call); |
|
719 } |
|
720 |
|
721 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) |
|
722 { |
|
723 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call |
|
724 // to a slot on the object. |
|
725 // |
|
726 // The call is delivered to the first slot that matches the following conditions: |
|
727 // - has the same name as the message's target member |
|
728 // - ALL of the message's types are found in slot's parameter list |
|
729 // - optionally has one more parameter of type QDBusMessage |
|
730 // If none match, then the slot of the same name as the message target and with |
|
731 // the first type of QDBusMessage is delivered. |
|
732 // |
|
733 // The D-Bus specification requires that all MethodCall messages be replied to, unless the |
|
734 // caller specifically waived this requirement. This means that we inspect if the user slot |
|
735 // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a |
|
736 // QDBusMessage parameter, it cannot generate a reply. |
|
737 // |
|
738 // When a return message is generated, the slot's return type, if any, will be placed |
|
739 // in the message's first position. If there are non-const reference parameters to the |
|
740 // slot, they must appear at the end and will be placed in the subsequent message |
|
741 // positions. |
|
742 |
|
743 static const char cachePropertyName[] = "_qdbus_slotCache"; |
|
744 |
|
745 if (!object) |
|
746 return false; |
|
747 |
|
748 Q_ASSERT_X(QThread::currentThread() == object->thread(), |
|
749 "QDBusConnection: internal threading error", |
|
750 "function called for an object that is in another thread!!"); |
|
751 |
|
752 QDBusSlotCache slotCache = |
|
753 qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName)); |
|
754 QString cacheKey = msg.member(), signature = msg.signature(); |
|
755 if (!signature.isEmpty()) { |
|
756 cacheKey.reserve(cacheKey.length() + 1 + signature.length()); |
|
757 cacheKey += QLatin1Char('.'); |
|
758 cacheKey += signature; |
|
759 } |
|
760 |
|
761 QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey); |
|
762 while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags && |
|
763 cacheIt.key() == cacheKey) |
|
764 ++cacheIt; |
|
765 if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey) |
|
766 { |
|
767 // not cached, analyse the meta object |
|
768 const QMetaObject *mo = object->metaObject(); |
|
769 QByteArray memberName = msg.member().toUtf8(); |
|
770 |
|
771 // find a slot that matches according to the rules above |
|
772 QDBusSlotCache::Data slotData; |
|
773 slotData.flags = flags; |
|
774 slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes); |
|
775 if (slotData.slotIdx == -1) { |
|
776 // ### this is where we want to add the connection as an arg too |
|
777 // try with no parameters, but with a QDBusMessage |
|
778 slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes); |
|
779 if (slotData.metaTypes.count() != 2 || |
|
780 slotData.metaTypes.at(1) != QDBusMetaTypeId::message) { |
|
781 // not found |
|
782 // save the negative lookup |
|
783 slotData.slotIdx = -1; |
|
784 slotData.metaTypes.clear(); |
|
785 slotCache.hash.insert(cacheKey, slotData); |
|
786 object->setProperty(cachePropertyName, qVariantFromValue(slotCache)); |
|
787 return false; |
|
788 } |
|
789 } |
|
790 |
|
791 // save to the cache |
|
792 slotCache.hash.insert(cacheKey, slotData); |
|
793 object->setProperty(cachePropertyName, qVariantFromValue(slotCache)); |
|
794 |
|
795 // found the slot to be called |
|
796 deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx); |
|
797 return true; |
|
798 } else if (cacheIt->slotIdx == -1) { |
|
799 // negative cache |
|
800 return false; |
|
801 } else { |
|
802 // use the cache |
|
803 deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx); |
|
804 return true; |
|
805 } |
|
806 } |
|
807 |
|
808 void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, |
|
809 const QList<int> &metaTypes, int slotIdx) |
|
810 { |
|
811 Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), |
|
812 "QDBusConnection: internal threading error", |
|
813 "function called for an object that is in another thread!!"); |
|
814 |
|
815 QVarLengthArray<void *, 10> params; |
|
816 params.reserve(metaTypes.count()); |
|
817 |
|
818 QVariantList auxParameters; |
|
819 // let's create the parameter list |
|
820 |
|
821 // first one is the return type -- add it below |
|
822 params.append(0); |
|
823 |
|
824 // add the input parameters |
|
825 int i; |
|
826 int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1); |
|
827 for (i = 1; i <= pCount; ++i) { |
|
828 int id = metaTypes[i]; |
|
829 if (id == QDBusMetaTypeId::message) |
|
830 break; |
|
831 |
|
832 const QVariant &arg = msg.arguments().at(i - 1); |
|
833 if (arg.userType() == id) |
|
834 // no conversion needed |
|
835 params.append(const_cast<void *>(arg.constData())); |
|
836 else if (arg.userType() == qMetaTypeId<QDBusArgument>()) { |
|
837 // convert to what the function expects |
|
838 void *null = 0; |
|
839 auxParameters.append(QVariant(id, null)); |
|
840 |
|
841 const QDBusArgument &in = |
|
842 *reinterpret_cast<const QDBusArgument *>(arg.constData()); |
|
843 QVariant &out = auxParameters[auxParameters.count() - 1]; |
|
844 |
|
845 if (!QDBusMetaType::demarshall(in, out.userType(), out.data())) |
|
846 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", |
|
847 out.typeName(), out.userType()); |
|
848 |
|
849 params.append(const_cast<void *>(out.constData())); |
|
850 } else { |
|
851 qFatal("Internal error: got invalid meta type %d (%s) " |
|
852 "when trying to convert to meta type %d (%s)", |
|
853 arg.userType(), QMetaType::typeName(arg.userType()), |
|
854 id, QMetaType::typeName(id)); |
|
855 } |
|
856 } |
|
857 |
|
858 bool takesMessage = false; |
|
859 if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message) { |
|
860 params.append(const_cast<void*>(static_cast<const void*>(&msg))); |
|
861 takesMessage = true; |
|
862 ++i; |
|
863 } |
|
864 |
|
865 // output arguments |
|
866 QVariantList outputArgs; |
|
867 void *null = 0; |
|
868 if (metaTypes[0] != QMetaType::Void) { |
|
869 QVariant arg(metaTypes[0], null); |
|
870 outputArgs.append( arg ); |
|
871 params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); |
|
872 } |
|
873 for ( ; i < metaTypes.count(); ++i) { |
|
874 QVariant arg(metaTypes[i], null); |
|
875 outputArgs.append( arg ); |
|
876 params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData())); |
|
877 } |
|
878 |
|
879 // make call: |
|
880 bool fail; |
|
881 if (!object) { |
|
882 fail = true; |
|
883 } else { |
|
884 // FIXME: save the old sender! |
|
885 QDBusContextPrivate context(QDBusConnection(this), msg); |
|
886 QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context); |
|
887 QDBusConnectionPrivate::setSender(this); |
|
888 |
|
889 QPointer<QObject> ptr = object; |
|
890 fail = object->qt_metacall(QMetaObject::InvokeMetaMethod, |
|
891 slotIdx, params.data()) >= 0; |
|
892 QDBusConnectionPrivate::setSender(0); |
|
893 // the object might be deleted in the slot |
|
894 if (!ptr.isNull()) |
|
895 QDBusContextPrivate::set(object, old); |
|
896 } |
|
897 |
|
898 // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent |
|
899 // yet. |
|
900 if (msg.isReplyRequired() && !msg.isDelayedReply()) { |
|
901 if (!fail) { |
|
902 // normal reply |
|
903 qDBusDebug() << QThread::currentThread() << "Automatically sending reply:" << outputArgs; |
|
904 send(msg.createReply(outputArgs)); |
|
905 } else { |
|
906 // generate internal error |
|
907 qWarning("Internal error: Failed to deliver message"); |
|
908 send(msg.createErrorReply(QDBusError::InternalError, |
|
909 QLatin1String("Failed to deliver message"))); |
|
910 } |
|
911 } |
|
912 |
|
913 return; |
|
914 } |
|
915 |
|
916 extern bool qDBusInitThreads(); |
|
917 |
|
918 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) |
|
919 : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0), |
|
920 watchAndTimeoutLock(QMutex::Recursive), |
|
921 rootNode(QString(QLatin1Char('/'))) |
|
922 { |
|
923 static const bool threads = q_dbus_threads_init_default(); |
|
924 static const int debugging = ::isDebugging = qgetenv("QDBUS_DEBUG").toInt(); |
|
925 Q_UNUSED(threads) |
|
926 Q_UNUSED(debugging) |
|
927 |
|
928 #ifdef QDBUS_THREAD_DEBUG |
|
929 if (debugging > 1) |
|
930 qdbusThreadDebug = qdbusDefaultThreadDebug; |
|
931 #endif |
|
932 |
|
933 QDBusMetaTypeId::init(); |
|
934 |
|
935 rootNode.flags = 0; |
|
936 |
|
937 connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)), |
|
938 this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); |
|
939 } |
|
940 |
|
941 QDBusConnectionPrivate::~QDBusConnectionPrivate() |
|
942 { |
|
943 if (thread() && thread() != QThread::currentThread()) |
|
944 qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " |
|
945 "Timer and socket errors will follow and the program will probably crash", |
|
946 qPrintable(name)); |
|
947 |
|
948 closeConnection(); |
|
949 rootNode.children.clear(); // free resources |
|
950 qDeleteAll(cachedMetaObjects); |
|
951 |
|
952 if (server) |
|
953 q_dbus_server_unref(server); |
|
954 if (connection) |
|
955 q_dbus_connection_unref(connection); |
|
956 |
|
957 connection = 0; |
|
958 server = 0; |
|
959 } |
|
960 |
|
961 void QDBusConnectionPrivate::deleteYourself() |
|
962 { |
|
963 if (thread() && thread() != QThread::currentThread()) { |
|
964 // last reference dropped while not in the correct thread |
|
965 // ask the correct thread to delete |
|
966 |
|
967 // note: since we're posting an event to another thread, we |
|
968 // must consider deleteLater() to take effect immediately |
|
969 deleteLater(); |
|
970 } else { |
|
971 delete this; |
|
972 } |
|
973 } |
|
974 |
|
975 void QDBusConnectionPrivate::closeConnection() |
|
976 { |
|
977 QDBusWriteLocker locker(CloseConnectionAction, this); |
|
978 ConnectionMode oldMode = mode; |
|
979 mode = InvalidMode; // prevent reentrancy |
|
980 baseService.clear(); |
|
981 |
|
982 if (oldMode == ServerMode) { |
|
983 if (server) { |
|
984 q_dbus_server_disconnect(server); |
|
985 } |
|
986 } else if (oldMode == ClientMode || oldMode == PeerMode) { |
|
987 if (connection) { |
|
988 q_dbus_connection_close(connection); |
|
989 // send the "close" message |
|
990 while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) |
|
991 ; |
|
992 } |
|
993 } |
|
994 } |
|
995 |
|
996 void QDBusConnectionPrivate::checkThread() |
|
997 { |
|
998 if (!thread()) { |
|
999 if (QCoreApplication::instance()) |
|
1000 moveToThread(QCoreApplication::instance()->thread()); |
|
1001 else |
|
1002 qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread", |
|
1003 qPrintable(name)); |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error) |
|
1008 { |
|
1009 if (!error) |
|
1010 return false; // no error |
|
1011 |
|
1012 //lock.lockForWrite(); |
|
1013 lastError = error; |
|
1014 //lock.unlock(); |
|
1015 return true; |
|
1016 } |
|
1017 |
|
1018 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) |
|
1019 { |
|
1020 { |
|
1021 QDBusWatchAndTimeoutLocker locker(TimerEventAction, this); |
|
1022 DBusTimeout *timeout = timeouts.value(e->timerId(), 0); |
|
1023 if (timeout) |
|
1024 q_dbus_timeout_handle(timeout); |
|
1025 } |
|
1026 |
|
1027 doDispatch(); |
|
1028 } |
|
1029 |
|
1030 void QDBusConnectionPrivate::customEvent(QEvent *e) |
|
1031 { |
|
1032 Q_ASSERT(e->type() == QEvent::User); |
|
1033 |
|
1034 QDBusConnectionCallbackEvent *ev = static_cast<QDBusConnectionCallbackEvent *>(e); |
|
1035 QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), |
|
1036 QDBusLockerBase::BeforeDeliver, this); |
|
1037 switch (ev->subtype) |
|
1038 { |
|
1039 case QDBusConnectionCallbackEvent::AddTimeout: { |
|
1040 QDBusWatchAndTimeoutLocker locker(RealAddTimeoutAction, this); |
|
1041 while (!timeoutsPendingAdd.isEmpty()) { |
|
1042 QPair<DBusTimeout *, int> entry = timeoutsPendingAdd.takeFirst(); |
|
1043 qDBusRealAddTimeout(this, entry.first, entry.second); |
|
1044 } |
|
1045 break; |
|
1046 } |
|
1047 |
|
1048 case QDBusConnectionCallbackEvent::KillTimer: |
|
1049 killTimer(ev->timerId); |
|
1050 break; |
|
1051 |
|
1052 case QDBusConnectionCallbackEvent::AddWatch: |
|
1053 qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd); |
|
1054 break; |
|
1055 |
|
1056 case QDBusConnectionCallbackEvent::ToggleWatch: |
|
1057 qDBusRealToggleWatch(this, ev->watch, ev->fd); |
|
1058 break; |
|
1059 } |
|
1060 QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), |
|
1061 QDBusLockerBase::AfterDeliver, this); |
|
1062 } |
|
1063 |
|
1064 void QDBusConnectionPrivate::doDispatch() |
|
1065 { |
|
1066 QDBusDispatchLocker locker(DoDispatchAction, this); |
|
1067 if (mode == ClientMode || mode == PeerMode) |
|
1068 while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; |
|
1069 } |
|
1070 |
|
1071 void QDBusConnectionPrivate::socketRead(int fd) |
|
1072 { |
|
1073 QVarLengthArray<DBusWatch *, 2> pendingWatches; |
|
1074 |
|
1075 { |
|
1076 QDBusWatchAndTimeoutLocker locker(SocketReadAction, this); |
|
1077 WatcherHash::ConstIterator it = watchers.constFind(fd); |
|
1078 while (it != watchers.constEnd() && it.key() == fd) { |
|
1079 if (it->watch && it->read && it->read->isEnabled()) |
|
1080 pendingWatches.append(it.value().watch); |
|
1081 ++it; |
|
1082 } |
|
1083 } |
|
1084 |
|
1085 for (int i = 0; i < pendingWatches.size(); ++i) |
|
1086 if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_READABLE)) |
|
1087 qDebug("OUT OF MEM"); |
|
1088 doDispatch(); |
|
1089 } |
|
1090 |
|
1091 void QDBusConnectionPrivate::socketWrite(int fd) |
|
1092 { |
|
1093 QVarLengthArray<DBusWatch *, 2> pendingWatches; |
|
1094 |
|
1095 { |
|
1096 QDBusWatchAndTimeoutLocker locker(SocketWriteAction, this); |
|
1097 WatcherHash::ConstIterator it = watchers.constFind(fd); |
|
1098 while (it != watchers.constEnd() && it.key() == fd) { |
|
1099 if (it->watch && it->write && it->write->isEnabled()) |
|
1100 pendingWatches.append(it.value().watch); |
|
1101 ++it; |
|
1102 } |
|
1103 } |
|
1104 |
|
1105 for (int i = 0; i < pendingWatches.size(); ++i) |
|
1106 if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_WRITABLE)) |
|
1107 qDebug("OUT OF MEM"); |
|
1108 } |
|
1109 |
|
1110 void QDBusConnectionPrivate::objectDestroyed(QObject *obj) |
|
1111 { |
|
1112 QDBusWriteLocker locker(ObjectDestroyedAction, this); |
|
1113 huntAndDestroy(obj, rootNode); |
|
1114 |
|
1115 SignalHookHash::iterator sit = signalHooks.begin(); |
|
1116 while (sit != signalHooks.end()) { |
|
1117 if (static_cast<QObject *>(sit.value().obj) == obj) |
|
1118 sit = disconnectSignal(sit); |
|
1119 else |
|
1120 ++sit; |
|
1121 } |
|
1122 |
|
1123 obj->disconnect(this); |
|
1124 } |
|
1125 |
|
1126 void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId, |
|
1127 const QVariantList &args) |
|
1128 { |
|
1129 QString interface = qDBusInterfaceFromMetaObject(mo); |
|
1130 |
|
1131 QMetaMethod mm = mo->method(signalId); |
|
1132 QByteArray memberName = mm.signature(); |
|
1133 memberName.truncate(memberName.indexOf('(')); |
|
1134 |
|
1135 // check if it's scriptable |
|
1136 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; |
|
1137 bool isAdaptor = false; |
|
1138 for ( ; mo; mo = mo->superClass()) |
|
1139 if (mo == &QDBusAbstractAdaptor::staticMetaObject) { |
|
1140 isAdaptor = true; |
|
1141 break; |
|
1142 } |
|
1143 |
|
1144 QDBusReadLocker locker(RelaySignalAction, this); |
|
1145 QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), interface, |
|
1146 QLatin1String(memberName)); |
|
1147 QDBusMessagePrivate::setParametersValidated(message, true); |
|
1148 message.setArguments(args); |
|
1149 QDBusError error; |
|
1150 DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, &error); |
|
1151 if (!msg) { |
|
1152 qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(), |
|
1153 qPrintable(error.message())); |
|
1154 lastError = error; |
|
1155 return; |
|
1156 } |
|
1157 |
|
1158 //qDBusDebug() << "Emitting signal" << message; |
|
1159 //qDBusDebug() << "for paths:"; |
|
1160 q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything |
|
1161 huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor); |
|
1162 q_dbus_message_unref(msg); |
|
1163 } |
|
1164 |
|
1165 void QDBusConnectionPrivate::_q_serviceOwnerChanged(const QString &name, |
|
1166 const QString &oldOwner, const QString &newOwner) |
|
1167 { |
|
1168 if (oldOwner == baseService) |
|
1169 unregisterService(name); |
|
1170 if (newOwner == baseService) |
|
1171 registerService(name); |
|
1172 |
|
1173 QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this); |
|
1174 QMutableHashIterator<QString, SignalHook> it(signalHooks); |
|
1175 it.toFront(); |
|
1176 while (it.hasNext()) |
|
1177 if (it.next().value().service == name) |
|
1178 it.value().owner = newOwner; |
|
1179 } |
|
1180 |
|
1181 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName, |
|
1182 QList<int> ¶ms) |
|
1183 { |
|
1184 int midx = obj->metaObject()->indexOfMethod(normalizedName); |
|
1185 if (midx == -1) |
|
1186 return -1; |
|
1187 |
|
1188 int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params); |
|
1189 if ( inputCount == -1 || inputCount + 1 != params.count() ) |
|
1190 return -1; // failed to parse or invalid arguments or output arguments |
|
1191 |
|
1192 return midx; |
|
1193 } |
|
1194 |
|
1195 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, |
|
1196 const QString &service, const QString &owner, |
|
1197 const QString &path, const QString &interface, const QString &name, |
|
1198 QObject *receiver, const char *signal, int minMIdx, |
|
1199 bool buildSignature) |
|
1200 { |
|
1201 QByteArray normalizedName = signal + 1; |
|
1202 hook.midx = findSlot(receiver, signal + 1, hook.params); |
|
1203 if (hook.midx == -1) { |
|
1204 normalizedName = QMetaObject::normalizedSignature(signal + 1); |
|
1205 hook.midx = findSlot(receiver, normalizedName, hook.params); |
|
1206 } |
|
1207 if (hook.midx < minMIdx) { |
|
1208 if (hook.midx == -1) |
|
1209 {} |
|
1210 return false; |
|
1211 } |
|
1212 |
|
1213 hook.service = service; |
|
1214 hook.owner = owner; // we don't care if the service has an owner yet |
|
1215 hook.path = path; |
|
1216 hook.obj = receiver; |
|
1217 |
|
1218 // build the D-Bus signal name and signature |
|
1219 // This should not happen for QDBusConnection::connect, use buildSignature here, since |
|
1220 // QDBusConnection::connect passes false and everything else uses true |
|
1221 QString mname = name; |
|
1222 if (buildSignature && mname.isNull()) { |
|
1223 normalizedName.truncate(normalizedName.indexOf('(')); |
|
1224 mname = QString::fromUtf8(normalizedName); |
|
1225 } |
|
1226 key = mname; |
|
1227 key.reserve(interface.length() + 1 + mname.length()); |
|
1228 key += QLatin1Char(':'); |
|
1229 key += interface; |
|
1230 |
|
1231 if (buildSignature) { |
|
1232 hook.signature.clear(); |
|
1233 for (int i = 1; i < hook.params.count(); ++i) |
|
1234 if (hook.params.at(i) != QDBusMetaTypeId::message) |
|
1235 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) ); |
|
1236 } |
|
1237 |
|
1238 hook.matchRule = buildMatchRule(service, owner, path, interface, mname, hook.signature); |
|
1239 return true; // connect to this signal |
|
1240 } |
|
1241 |
|
1242 void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code) |
|
1243 { |
|
1244 if (code == QDBusError::UnknownMethod) { |
|
1245 QString interfaceMsg; |
|
1246 if (msg.interface().isEmpty()) |
|
1247 interfaceMsg = QLatin1String("any interface"); |
|
1248 else |
|
1249 interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface()); |
|
1250 |
|
1251 send(msg.createErrorReply(code, |
|
1252 QString::fromLatin1("No such method '%1' in %2 at object path '%3' " |
|
1253 "(signature '%4')") |
|
1254 .arg(msg.member(), interfaceMsg, msg.path(), msg.signature()))); |
|
1255 } else if (code == QDBusError::UnknownInterface) { |
|
1256 send(msg.createErrorReply(QDBusError::UnknownInterface, |
|
1257 QString::fromLatin1("No such interface '%1' at object path '%2'") |
|
1258 .arg(msg.interface(), msg.path()))); |
|
1259 } else if (code == QDBusError::UnknownObject) { |
|
1260 send(msg.createErrorReply(QDBusError::UnknownObject, |
|
1261 QString::fromLatin1("No such object path '%1'").arg(msg.path()))); |
|
1262 } |
|
1263 } |
|
1264 |
|
1265 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node, |
|
1266 const QDBusMessage &msg) |
|
1267 { |
|
1268 // object may be null |
|
1269 const QString interface = msg.interface(); |
|
1270 |
|
1271 if (interface.isEmpty() || interface == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) { |
|
1272 if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) { |
|
1273 //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg; |
|
1274 QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node)); |
|
1275 send(reply); |
|
1276 return true; |
|
1277 } |
|
1278 |
|
1279 if (!interface.isEmpty()) { |
|
1280 sendError(msg, QDBusError::UnknownMethod); |
|
1281 return true; |
|
1282 } |
|
1283 } |
|
1284 |
|
1285 if (node.obj && (interface.isEmpty() || |
|
1286 interface == QLatin1String(DBUS_INTERFACE_PROPERTIES))) { |
|
1287 //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg; |
|
1288 if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) { |
|
1289 QDBusMessage reply = qDBusPropertyGet(node, msg); |
|
1290 send(reply); |
|
1291 return true; |
|
1292 } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) { |
|
1293 QDBusMessage reply = qDBusPropertySet(node, msg); |
|
1294 send(reply); |
|
1295 return true; |
|
1296 } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) { |
|
1297 QDBusMessage reply = qDBusPropertyGetAll(node, msg); |
|
1298 send(reply); |
|
1299 return true; |
|
1300 } |
|
1301 |
|
1302 if (!interface.isEmpty()) { |
|
1303 sendError(msg, QDBusError::UnknownMethod); |
|
1304 return true; |
|
1305 } |
|
1306 } |
|
1307 |
|
1308 return false; |
|
1309 } |
|
1310 |
|
1311 void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg, |
|
1312 int pathStartPos) |
|
1313 { |
|
1314 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot |
|
1315 // on the object. |
|
1316 // |
|
1317 // The call is routed through the adaptor sub-objects if we have any |
|
1318 |
|
1319 // object may be null |
|
1320 |
|
1321 if (pathStartPos != msg.path().length()) { |
|
1322 node.flags &= ~QDBusConnection::ExportAllSignals; |
|
1323 node.obj = findChildObject(&node, msg.path(), pathStartPos); |
|
1324 if (!node.obj) { |
|
1325 sendError(msg, QDBusError::UnknownObject); |
|
1326 return; |
|
1327 } |
|
1328 } |
|
1329 |
|
1330 QDBusAdaptorConnector *connector; |
|
1331 if (node.flags & QDBusConnection::ExportAdaptors && |
|
1332 (connector = qDBusFindAdaptorConnector(node.obj))) { |
|
1333 int newflags = node.flags | QDBusConnection::ExportAllSlots; |
|
1334 |
|
1335 if (msg.interface().isEmpty()) { |
|
1336 // place the call in all interfaces |
|
1337 // let the first one that handles it to work |
|
1338 QDBusAdaptorConnector::AdaptorMap::ConstIterator it = |
|
1339 connector->adaptors.constBegin(); |
|
1340 QDBusAdaptorConnector::AdaptorMap::ConstIterator end = |
|
1341 connector->adaptors.constEnd(); |
|
1342 |
|
1343 for ( ; it != end; ++it) |
|
1344 if (activateCall(it->adaptor, newflags, msg)) |
|
1345 return; |
|
1346 } else { |
|
1347 // check if we have an interface matching the name that was asked: |
|
1348 QDBusAdaptorConnector::AdaptorMap::ConstIterator it; |
|
1349 it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), |
|
1350 msg.interface()); |
|
1351 if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) { |
|
1352 if (!activateCall(it->adaptor, newflags, msg)) |
|
1353 sendError(msg, QDBusError::UnknownMethod); |
|
1354 return; |
|
1355 } |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 // no adaptors matched or were exported |
|
1360 // try our standard filters |
|
1361 if (activateInternalFilters(node, msg)) |
|
1362 return; // internal filters have already run or an error has been sent |
|
1363 |
|
1364 // try the object itself: |
|
1365 if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots)) { |
|
1366 bool interfaceFound = true; |
|
1367 if (!msg.interface().isEmpty()) |
|
1368 interfaceFound = qDBusInterfaceInObject(node.obj, msg.interface()); |
|
1369 |
|
1370 if (interfaceFound) { |
|
1371 if (!activateCall(node.obj, node.flags, msg)) |
|
1372 sendError(msg, QDBusError::UnknownMethod); |
|
1373 return; |
|
1374 } |
|
1375 } |
|
1376 |
|
1377 // nothing matched, send an error code |
|
1378 if (msg.interface().isEmpty()) |
|
1379 sendError(msg, QDBusError::UnknownMethod); |
|
1380 else |
|
1381 sendError(msg, QDBusError::UnknownInterface); |
|
1382 } |
|
1383 |
|
1384 void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) |
|
1385 { |
|
1386 // if the msg is external, we were called from inside doDispatch |
|
1387 // that means the dispatchLock mutex is locked |
|
1388 // must not call out to user code in that case |
|
1389 // |
|
1390 // however, if the message is internal, handleMessage was called |
|
1391 // directly and no lock is in place. We can therefore call out to |
|
1392 // user code, if necessary |
|
1393 ObjectTreeNode result; |
|
1394 int usedLength; |
|
1395 QThread *objThread = 0; |
|
1396 QSemaphore sem; |
|
1397 bool semWait; |
|
1398 |
|
1399 { |
|
1400 QDBusReadLocker locker(HandleObjectCallAction, this); |
|
1401 if (!findObject(&rootNode, msg.path(), usedLength, result)) { |
|
1402 // qDebug("Call failed: no object found at %s", qPrintable(msg.path())); |
|
1403 sendError(msg, QDBusError::UnknownObject); |
|
1404 return; |
|
1405 } |
|
1406 |
|
1407 if (!result.obj) { |
|
1408 // no object -> no threading issues |
|
1409 // it's either going to be an error, or an internal filter |
|
1410 activateObject(result, msg, usedLength); |
|
1411 return; |
|
1412 } |
|
1413 |
|
1414 objThread = result.obj->thread(); |
|
1415 if (!objThread) { |
|
1416 send(msg.createErrorReply(QDBusError::InternalError, |
|
1417 QString::fromLatin1("Object '%1' (at path '%2')" |
|
1418 " has no thread. Cannot deliver message.") |
|
1419 .arg(result.obj->objectName(), msg.path()))); |
|
1420 return; |
|
1421 } |
|
1422 |
|
1423 if (!QDBusMessagePrivate::isLocal(msg)) { |
|
1424 // external incoming message |
|
1425 // post it and forget |
|
1426 postEventToThread(HandleObjectCallPostEventAction, result.obj, |
|
1427 new QDBusActivateObjectEvent(QDBusConnection(this), this, result, |
|
1428 usedLength, msg)); |
|
1429 return; |
|
1430 } else if (objThread != QThread::currentThread()) { |
|
1431 // synchronize with other thread |
|
1432 postEventToThread(HandleObjectCallPostEventAction, result.obj, |
|
1433 new QDBusActivateObjectEvent(QDBusConnection(this), this, result, |
|
1434 usedLength, msg, &sem)); |
|
1435 semWait = true; |
|
1436 } else { |
|
1437 semWait = false; |
|
1438 } |
|
1439 } // release the lock |
|
1440 |
|
1441 if (semWait) |
|
1442 SEM_ACQUIRE(HandleObjectCallSemaphoreAction, sem); |
|
1443 else |
|
1444 activateObject(result, msg, usedLength); |
|
1445 } |
|
1446 |
|
1447 QDBusActivateObjectEvent::~QDBusActivateObjectEvent() |
|
1448 { |
|
1449 if (!handled) { |
|
1450 // we're being destroyed without delivering |
|
1451 // it means the object was deleted between posting and delivering |
|
1452 QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); |
|
1453 that->sendError(message, QDBusError::UnknownObject); |
|
1454 } |
|
1455 |
|
1456 // semaphore releasing happens in ~QMetaCallEvent |
|
1457 } |
|
1458 |
|
1459 int QDBusActivateObjectEvent::placeMetaCall(QObject *) |
|
1460 { |
|
1461 QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); |
|
1462 |
|
1463 QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, |
|
1464 QDBusLockerBase::BeforeDeliver, that); |
|
1465 that->activateObject(node, message, pathStartPos); |
|
1466 QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, |
|
1467 QDBusLockerBase::AfterDeliver, that); |
|
1468 |
|
1469 handled = true; |
|
1470 return -1; |
|
1471 } |
|
1472 |
|
1473 void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg) |
|
1474 { |
|
1475 SignalHookHash::const_iterator it = signalHooks.find(key); |
|
1476 SignalHookHash::const_iterator end = signalHooks.constEnd(); |
|
1477 //qDebug("looking for: %s", path.toLocal8Bit().constData()); |
|
1478 //qDBusDebug() << signalHooks.keys(); |
|
1479 for ( ; it != end && it.key() == key; ++it) { |
|
1480 const SignalHook &hook = it.value(); |
|
1481 if (!hook.owner.isEmpty() && hook.owner != msg.service()) |
|
1482 continue; |
|
1483 if (!hook.path.isEmpty() && hook.path != msg.path()) |
|
1484 continue; |
|
1485 if (!hook.signature.isEmpty() && hook.signature != msg.signature()) |
|
1486 continue; |
|
1487 if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty()) |
|
1488 continue; |
|
1489 |
|
1490 activateSignal(hook, msg); |
|
1491 } |
|
1492 } |
|
1493 |
|
1494 void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) |
|
1495 { |
|
1496 // We call handlesignal(QString, QDBusMessage) three times: |
|
1497 // one with member:interface |
|
1498 // one with member: |
|
1499 // one with :interface |
|
1500 // This allows us to match signals with wildcards on member or interface |
|
1501 // (but not both) |
|
1502 |
|
1503 QString key = msg.member(); |
|
1504 key.reserve(key.length() + 1 + msg.interface().length()); |
|
1505 key += QLatin1Char(':'); |
|
1506 key += msg.interface(); |
|
1507 |
|
1508 QDBusReadLocker locker(HandleSignalAction, this); |
|
1509 handleSignal(key, msg); // one try |
|
1510 |
|
1511 key.truncate(msg.member().length() + 1); // keep the ':' |
|
1512 handleSignal(key, msg); // second try |
|
1513 |
|
1514 key = QLatin1Char(':'); |
|
1515 key += msg.interface(); |
|
1516 handleSignal(key, msg); // third try |
|
1517 } |
|
1518 |
|
1519 static dbus_int32_t server_slot = -1; |
|
1520 |
|
1521 void QDBusConnectionPrivate::setServer(DBusServer *s, const QDBusErrorInternal &error) |
|
1522 { |
|
1523 if (!s) { |
|
1524 handleError(error); |
|
1525 return; |
|
1526 } |
|
1527 |
|
1528 server = s; |
|
1529 mode = ServerMode; |
|
1530 |
|
1531 dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot); |
|
1532 if (data_allocated && server_slot < 0) |
|
1533 return; |
|
1534 |
|
1535 dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server, |
|
1536 qDBusAddWatch, |
|
1537 qDBusRemoveWatch, |
|
1538 qDBusToggleWatch, |
|
1539 this, 0); |
|
1540 //qDebug() << "watch_functions_set" << watch_functions_set; |
|
1541 Q_UNUSED(watch_functions_set); |
|
1542 |
|
1543 dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server, |
|
1544 qDBusAddTimeout, |
|
1545 qDBusRemoveTimeout, |
|
1546 qDBusToggleTimeout, |
|
1547 this, 0); |
|
1548 //qDebug() << "time_functions_set" << time_functions_set; |
|
1549 Q_UNUSED(time_functions_set); |
|
1550 |
|
1551 q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0); |
|
1552 |
|
1553 dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, 0); |
|
1554 //qDebug() << "data_set" << data_set; |
|
1555 Q_UNUSED(data_set); |
|
1556 } |
|
1557 |
|
1558 void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error) |
|
1559 { |
|
1560 if (!c) { |
|
1561 handleError(error); |
|
1562 return; |
|
1563 } |
|
1564 |
|
1565 connection = c; |
|
1566 mode = PeerMode; |
|
1567 |
|
1568 q_dbus_connection_set_exit_on_disconnect(connection, false); |
|
1569 q_dbus_connection_set_watch_functions(connection, |
|
1570 qDBusAddWatch, |
|
1571 qDBusRemoveWatch, |
|
1572 qDBusToggleWatch, |
|
1573 this, 0); |
|
1574 q_dbus_connection_set_timeout_functions(connection, |
|
1575 qDBusAddTimeout, |
|
1576 qDBusRemoveTimeout, |
|
1577 qDBusToggleTimeout, |
|
1578 this, 0); |
|
1579 q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); |
|
1580 q_dbus_connection_add_filter(connection, |
|
1581 qDBusSignalFilter, |
|
1582 this, 0); |
|
1583 |
|
1584 QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); |
|
1585 } |
|
1586 |
|
1587 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error) |
|
1588 { |
|
1589 if (!dbc) { |
|
1590 handleError(error); |
|
1591 return; |
|
1592 } |
|
1593 |
|
1594 connection = dbc; |
|
1595 mode = ClientMode; |
|
1596 |
|
1597 q_dbus_connection_set_exit_on_disconnect(connection, false); |
|
1598 q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch, |
|
1599 qDBusToggleWatch, this, 0); |
|
1600 q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout, |
|
1601 qDBusToggleTimeout, this, 0); |
|
1602 q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); |
|
1603 |
|
1604 // Initialize the match rules |
|
1605 // We want all messages that have us as destination |
|
1606 // signals don't have destinations, but connectSignal() takes care of them |
|
1607 const char *service = q_dbus_bus_get_unique_name(connection); |
|
1608 if (service) { |
|
1609 QVarLengthArray<char, 56> filter; |
|
1610 filter.append("destination='", 13); |
|
1611 filter.append(service, qstrlen(service)); |
|
1612 filter.append("\'\0", 2); |
|
1613 |
|
1614 QDBusErrorInternal error; |
|
1615 q_dbus_bus_add_match(connection, filter.constData(), error); |
|
1616 if (handleError(error)) { |
|
1617 closeConnection(); |
|
1618 return; |
|
1619 } |
|
1620 |
|
1621 baseService = QString::fromUtf8(service); |
|
1622 } else { |
|
1623 qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service"); |
|
1624 } |
|
1625 |
|
1626 q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0); |
|
1627 |
|
1628 //qDebug("base service: %s", service); |
|
1629 |
|
1630 // schedule a dispatch: |
|
1631 QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); |
|
1632 } |
|
1633 |
|
1634 extern "C"{ |
|
1635 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) |
|
1636 { |
|
1637 QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data); |
|
1638 Q_ASSERT(call->pending == pending); |
|
1639 Q_UNUSED(pending); |
|
1640 QDBusConnectionPrivate::processFinishedCall(call); |
|
1641 } |
|
1642 } |
|
1643 |
|
1644 void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall) |
|
1645 { |
|
1646 Q_ASSERT(pcall->pending); |
|
1647 QDBusDispatchLocker locker(PendingCallBlockAction, this); |
|
1648 q_dbus_pending_call_block(pcall->pending); |
|
1649 // QDBusConnectionPrivate::processFinishedCall() is called automatically |
|
1650 } |
|
1651 |
|
1652 void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) |
|
1653 { |
|
1654 QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection); |
|
1655 |
|
1656 QDBusMessage &msg = call->replyMessage; |
|
1657 if (call->pending) { |
|
1658 // decode the message |
|
1659 DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending); |
|
1660 msg = QDBusMessagePrivate::fromDBusMessage(reply); |
|
1661 q_dbus_message_unref(reply); |
|
1662 } |
|
1663 qDBusDebug() << QThread::currentThread() << "got message reply (async):" << msg; |
|
1664 |
|
1665 // Check if the reply has the expected signature |
|
1666 call->checkReceivedSignature(); |
|
1667 |
|
1668 if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) { |
|
1669 // Deliver the return values of a remote function call. |
|
1670 // |
|
1671 // There is only one connection and it is specified by idx |
|
1672 // The slot must have the same parameter types that the message does |
|
1673 // The slot may have less parameters than the message |
|
1674 // The slot may optionally have one final parameter that is QDBusMessage |
|
1675 // The slot receives read-only copies of the message (i.e., pass by value or by const-ref) |
|
1676 |
|
1677 QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx, |
|
1678 call->metaTypes, msg); |
|
1679 if (e) |
|
1680 connection->postEventToThread(MessageResultReceivedAction, call->receiver, e); |
|
1681 else |
|
1682 qDBusDebug() << "Deliver failed!"; |
|
1683 } |
|
1684 |
|
1685 // Are there any watchers? |
|
1686 if (call->watcherHelper) |
|
1687 call->watcherHelper->emitSignals(msg, call->sentMessage); |
|
1688 |
|
1689 if (msg.type() == QDBusMessage::ErrorMessage) |
|
1690 emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage); |
|
1691 |
|
1692 if (call->pending) |
|
1693 q_dbus_pending_call_unref(call->pending); |
|
1694 call->pending = 0; |
|
1695 |
|
1696 if (call->autoDelete) |
|
1697 delete call; |
|
1698 } |
|
1699 |
|
1700 int QDBusConnectionPrivate::send(const QDBusMessage& message) |
|
1701 { |
|
1702 if (QDBusMessagePrivate::isLocal(message)) |
|
1703 return -1; // don't send; the reply will be retrieved by the caller |
|
1704 // through the d_ptr->localReply link |
|
1705 |
|
1706 QDBusError error; |
|
1707 DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, &error); |
|
1708 if (!msg) { |
|
1709 if (message.type() == QDBusMessage::MethodCallMessage) |
|
1710 qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", |
|
1711 qPrintable(message.service()), qPrintable(message.path()), |
|
1712 qPrintable(message.interface()), qPrintable(message.member()), |
|
1713 qPrintable(error.message())); |
|
1714 else if (message.type() == QDBusMessage::SignalMessage) |
|
1715 qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\": %s", |
|
1716 qPrintable(message.path()), qPrintable(message.interface()), |
|
1717 qPrintable(message.member()), |
|
1718 qPrintable(error.message())); |
|
1719 else |
|
1720 qWarning("QDBusConnection: error: could not send %s message to service \"%s\": %s", |
|
1721 message.type() == QDBusMessage::ReplyMessage ? "reply" : |
|
1722 message.type() == QDBusMessage::ErrorMessage ? "error" : |
|
1723 "invalid", qPrintable(message.service()), |
|
1724 qPrintable(error.message())); |
|
1725 lastError = error; |
|
1726 return 0; |
|
1727 } |
|
1728 |
|
1729 q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything |
|
1730 |
|
1731 qDBusDebug() << QThread::currentThread() << "sending message (no reply):" << message; |
|
1732 checkThread(); |
|
1733 bool isOk = q_dbus_connection_send(connection, msg, 0); |
|
1734 int serial = 0; |
|
1735 if (isOk) |
|
1736 serial = q_dbus_message_get_serial(msg); |
|
1737 |
|
1738 q_dbus_message_unref(msg); |
|
1739 return serial; |
|
1740 } |
|
1741 |
|
1742 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, |
|
1743 int sendMode, int timeout) |
|
1744 { |
|
1745 checkThread(); |
|
1746 if ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block) |
|
1747 && isServiceRegisteredByThread(message.service())) |
|
1748 // special case for synchronous local calls |
|
1749 return sendWithReplyLocal(message); |
|
1750 |
|
1751 if (!QCoreApplication::instance() || sendMode == QDBus::Block) { |
|
1752 QDBusError err; |
|
1753 DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, &err); |
|
1754 if (!msg) { |
|
1755 qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", |
|
1756 qPrintable(message.service()), qPrintable(message.path()), |
|
1757 qPrintable(message.interface()), qPrintable(message.member()), |
|
1758 qPrintable(err.message())); |
|
1759 lastError = err; |
|
1760 return QDBusMessage::createError(err); |
|
1761 } |
|
1762 |
|
1763 qDBusDebug() << QThread::currentThread() << "sending message (blocking):" << message; |
|
1764 QDBusErrorInternal error; |
|
1765 DBusMessage *reply = q_dbus_connection_send_with_reply_and_block(connection, msg, timeout, error); |
|
1766 |
|
1767 q_dbus_message_unref(msg); |
|
1768 |
|
1769 if (!!error) { |
|
1770 lastError = err = error; |
|
1771 return QDBusMessage::createError(err); |
|
1772 } |
|
1773 |
|
1774 QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(reply); |
|
1775 q_dbus_message_unref(reply); |
|
1776 qDBusDebug() << QThread::currentThread() << "got message reply (blocking):" << amsg; |
|
1777 |
|
1778 return amsg; |
|
1779 } else { // use the event loop |
|
1780 QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout); |
|
1781 Q_ASSERT(pcall); |
|
1782 |
|
1783 if (pcall->replyMessage.type() != QDBusMessage::InvalidMessage) { |
|
1784 pcall->watcherHelper = new QDBusPendingCallWatcherHelper; |
|
1785 QEventLoop loop; |
|
1786 loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit())); |
|
1787 loop.connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), SLOT(quit())); |
|
1788 |
|
1789 // enter the event loop and wait for a reply |
|
1790 loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); |
|
1791 } |
|
1792 |
|
1793 QDBusMessage reply = pcall->replyMessage; |
|
1794 lastError = reply; // set or clear error |
|
1795 |
|
1796 delete pcall; |
|
1797 return reply; |
|
1798 } |
|
1799 } |
|
1800 |
|
1801 QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message) |
|
1802 { |
|
1803 qDBusDebug() << QThread::currentThread() << "sending message via local-loop:" << message; |
|
1804 |
|
1805 QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message); |
|
1806 bool handled = handleMessage(localCallMsg); |
|
1807 |
|
1808 if (!handled) { |
|
1809 QString interface = message.interface(); |
|
1810 if (interface.isEmpty()) |
|
1811 interface = QLatin1String("<no-interface>"); |
|
1812 return QDBusMessage::createError(QDBusError::InternalError, |
|
1813 QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'") |
|
1814 .arg(interface, message.member(), |
|
1815 message.path(), message.signature())); |
|
1816 } |
|
1817 |
|
1818 // if the message was handled, there might be a reply |
|
1819 QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg); |
|
1820 if (localReplyMsg.type() == QDBusMessage::InvalidMessage) { |
|
1821 qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') " |
|
1822 "on blocking mode", qPrintable(message.member()), qPrintable(message.path()), |
|
1823 qPrintable(message.signature())); |
|
1824 return QDBusMessage::createError( |
|
1825 QDBusError(QDBusError::InternalError, |
|
1826 QLatin1String("local-loop message cannot have delayed replies"))); |
|
1827 } |
|
1828 |
|
1829 // there is a reply |
|
1830 qDBusDebug() << QThread::currentThread() << "got message via local-loop:" << localReplyMsg; |
|
1831 return localReplyMsg; |
|
1832 } |
|
1833 |
|
1834 QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, |
|
1835 int timeout) |
|
1836 { |
|
1837 if (isServiceRegisteredByThread(message.service())) { |
|
1838 // special case for local calls |
|
1839 QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate; |
|
1840 pcall->sentMessage = message; |
|
1841 pcall->replyMessage = sendWithReplyLocal(message); |
|
1842 pcall->connection = this; |
|
1843 |
|
1844 return pcall; |
|
1845 } |
|
1846 |
|
1847 checkThread(); |
|
1848 QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate; |
|
1849 pcall->sentMessage = message; |
|
1850 pcall->ref = 0; |
|
1851 |
|
1852 QDBusError error; |
|
1853 DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, &error); |
|
1854 if (!msg) { |
|
1855 qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", |
|
1856 qPrintable(message.service()), qPrintable(message.path()), |
|
1857 qPrintable(message.interface()), qPrintable(message.member()), |
|
1858 qPrintable(error.message())); |
|
1859 pcall->replyMessage = QDBusMessage::createError(error); |
|
1860 lastError = error; |
|
1861 return pcall; |
|
1862 } |
|
1863 |
|
1864 qDBusDebug() << QThread::currentThread() << "sending message (async):" << message; |
|
1865 DBusPendingCall *pending = 0; |
|
1866 |
|
1867 QDBusDispatchLocker locker(SendWithReplyAsyncAction, this); |
|
1868 if (q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) { |
|
1869 if (pending) { |
|
1870 q_dbus_message_unref(msg); |
|
1871 |
|
1872 pcall->pending = pending; |
|
1873 pcall->connection = this; |
|
1874 q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0); |
|
1875 |
|
1876 return pcall; |
|
1877 } else { |
|
1878 // we're probably disconnected at this point |
|
1879 lastError = error = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to server")); |
|
1880 } |
|
1881 } else { |
|
1882 lastError = error = QDBusError(QDBusError::NoMemory, QLatin1String("Out of memory")); |
|
1883 } |
|
1884 |
|
1885 q_dbus_message_unref(msg); |
|
1886 pcall->replyMessage = QDBusMessage::createError(error); |
|
1887 return pcall; |
|
1888 } |
|
1889 |
|
1890 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, |
|
1891 const char *returnMethod, const char *errorMethod, |
|
1892 int timeout) |
|
1893 { |
|
1894 QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout); |
|
1895 Q_ASSERT(pcall); |
|
1896 |
|
1897 // has it already finished (dispatched locally)? |
|
1898 if (pcall->replyMessage.type() == QDBusMessage::ReplyMessage) { |
|
1899 pcall->setReplyCallback(receiver, returnMethod); |
|
1900 processFinishedCall(pcall); |
|
1901 delete pcall; |
|
1902 return 1; |
|
1903 } |
|
1904 |
|
1905 // has it already finished and is an error reply message? |
|
1906 if (pcall->replyMessage.type() == QDBusMessage::ErrorMessage) { |
|
1907 if (errorMethod) { |
|
1908 pcall->watcherHelper = new QDBusPendingCallWatcherHelper; |
|
1909 connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod); |
|
1910 pcall->watcherHelper->moveToThread(thread()); |
|
1911 } |
|
1912 processFinishedCall(pcall); |
|
1913 delete pcall; |
|
1914 return 1; |
|
1915 } |
|
1916 |
|
1917 // has it already finished with error? |
|
1918 if (pcall->replyMessage.type() != QDBusMessage::InvalidMessage) { |
|
1919 delete pcall; |
|
1920 return 0; |
|
1921 } |
|
1922 |
|
1923 pcall->autoDelete = true; |
|
1924 pcall->ref.ref(); |
|
1925 |
|
1926 pcall->setReplyCallback(receiver, returnMethod); |
|
1927 if (errorMethod) { |
|
1928 pcall->watcherHelper = new QDBusPendingCallWatcherHelper; |
|
1929 connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod); |
|
1930 pcall->watcherHelper->moveToThread(thread()); |
|
1931 } |
|
1932 |
|
1933 return 1; |
|
1934 } |
|
1935 |
|
1936 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) |
|
1937 { |
|
1938 signalHooks.insertMulti(key, hook); |
|
1939 connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), |
|
1940 Qt::DirectConnection); |
|
1941 |
|
1942 MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule); |
|
1943 |
|
1944 if (it != matchRefCounts.end()) { // Match already present |
|
1945 it.value() = it.value() + 1; |
|
1946 return; |
|
1947 } |
|
1948 |
|
1949 matchRefCounts.insert(hook.matchRule, 1); |
|
1950 |
|
1951 if (connection) { |
|
1952 qDBusDebug("Adding rule: %s", hook.matchRule.constData()); |
|
1953 QDBusErrorInternal error; |
|
1954 q_dbus_bus_add_match(connection, hook.matchRule, error); |
|
1955 if (!!error) { |
|
1956 QDBusError qerror = error; |
|
1957 qWarning("QDBusConnectionPrivate::connectSignal: received error from D-Bus server " |
|
1958 "while connecting signal to %s::%s: %s (%s)", |
|
1959 hook.obj->metaObject()->className(), |
|
1960 hook.obj->metaObject()->method(hook.midx).signature(), |
|
1961 qPrintable(qerror.name()), qPrintable(qerror.message())); |
|
1962 Q_ASSERT(false); |
|
1963 } |
|
1964 } |
|
1965 } |
|
1966 |
|
1967 QDBusConnectionPrivate::SignalHookHash::Iterator |
|
1968 QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) |
|
1969 { |
|
1970 const SignalHook &hook = it.value(); |
|
1971 |
|
1972 bool erase = false; |
|
1973 MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule); |
|
1974 if (i == matchRefCounts.end()) { |
|
1975 qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!"); |
|
1976 } else { |
|
1977 if (i.value() == 1) { |
|
1978 erase = true; |
|
1979 matchRefCounts.erase(i); |
|
1980 } |
|
1981 else { |
|
1982 i.value() = i.value() - 1; |
|
1983 } |
|
1984 } |
|
1985 |
|
1986 // we don't care about errors here |
|
1987 if (connection && erase) { |
|
1988 qDBusDebug("Removing rule: %s", hook.matchRule.constData()); |
|
1989 q_dbus_bus_remove_match(connection, hook.matchRule, NULL); |
|
1990 } |
|
1991 |
|
1992 return signalHooks.erase(it); |
|
1993 } |
|
1994 |
|
1995 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) |
|
1996 { |
|
1997 connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), |
|
1998 Qt::DirectConnection); |
|
1999 |
|
2000 if (node->flags & (QDBusConnection::ExportAdaptors |
|
2001 | QDBusConnection::ExportScriptableSignals |
|
2002 | QDBusConnection::ExportNonScriptableSignals)) { |
|
2003 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj); |
|
2004 |
|
2005 if (node->flags & (QDBusConnection::ExportScriptableSignals |
|
2006 | QDBusConnection::ExportNonScriptableSignals)) { |
|
2007 connector->disconnectAllSignals(node->obj); |
|
2008 connector->connectAllSignals(node->obj); |
|
2009 } |
|
2010 |
|
2011 // disconnect and reconnect to avoid duplicates |
|
2012 connector->disconnect(SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), |
|
2013 this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList))); |
|
2014 connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), |
|
2015 this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), |
|
2016 Qt::DirectConnection); |
|
2017 } |
|
2018 } |
|
2019 |
|
2020 void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &owner, |
|
2021 const QString &path, const QString &interface, |
|
2022 QDBusAbstractInterface *receiver, |
|
2023 const char *signal) |
|
2024 { |
|
2025 // this function is called by QDBusAbstractInterface when one of its signals is connected |
|
2026 // we set up a relay from D-Bus into it |
|
2027 SignalHook hook; |
|
2028 QString key; |
|
2029 |
|
2030 if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, |
|
2031 QDBusAbstractInterface::staticMetaObject.methodCount(), true)) |
|
2032 return; // don't connect |
|
2033 |
|
2034 // add it to our list: |
|
2035 QDBusWriteLocker locker(ConnectRelayAction, this); |
|
2036 SignalHookHash::ConstIterator it = signalHooks.find(key); |
|
2037 SignalHookHash::ConstIterator end = signalHooks.constEnd(); |
|
2038 for ( ; it != end && it.key() == key; ++it) { |
|
2039 const SignalHook &entry = it.value(); |
|
2040 if (entry.service == hook.service && |
|
2041 entry.owner == hook.owner && |
|
2042 entry.path == hook.path && |
|
2043 entry.signature == hook.signature && |
|
2044 entry.obj == hook.obj && |
|
2045 entry.midx == hook.midx) |
|
2046 return; // already there, no need to re-add |
|
2047 } |
|
2048 |
|
2049 connectSignal(key, hook); |
|
2050 } |
|
2051 |
|
2052 void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &owner, |
|
2053 const QString &path, const QString &interface, |
|
2054 QDBusAbstractInterface *receiver, |
|
2055 const char *signal) |
|
2056 { |
|
2057 // this function is called by QDBusAbstractInterface when one of its signals is disconnected |
|
2058 // we remove relay from D-Bus into it |
|
2059 SignalHook hook; |
|
2060 QString key; |
|
2061 |
|
2062 if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, |
|
2063 QDBusAbstractInterface::staticMetaObject.methodCount(), true)) |
|
2064 return; // don't connect |
|
2065 |
|
2066 // remove it from our list: |
|
2067 QDBusWriteLocker locker(DisconnectRelayAction, this); |
|
2068 SignalHookHash::Iterator it = signalHooks.find(key); |
|
2069 SignalHookHash::Iterator end = signalHooks.end(); |
|
2070 for ( ; it != end && it.key() == key; ++it) { |
|
2071 const SignalHook &entry = it.value(); |
|
2072 if (entry.service == hook.service && |
|
2073 entry.owner == hook.owner && |
|
2074 entry.path == hook.path && |
|
2075 entry.signature == hook.signature && |
|
2076 entry.obj == hook.obj && |
|
2077 entry.midx == hook.midx) { |
|
2078 // found it |
|
2079 disconnectSignal(it); |
|
2080 return; |
|
2081 } |
|
2082 } |
|
2083 |
|
2084 qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found"); |
|
2085 } |
|
2086 |
|
2087 QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName) |
|
2088 { |
|
2089 if (QDBusUtil::isValidUniqueConnectionName(serviceName)) |
|
2090 return serviceName; |
|
2091 if (!connection || !QDBusUtil::isValidBusName(serviceName)) |
|
2092 return QString(); |
|
2093 |
|
2094 QDBusMessage msg = QDBusMessage::createMethodCall(QLatin1String(DBUS_SERVICE_DBUS), |
|
2095 QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS), |
|
2096 QLatin1String("GetNameOwner")); |
|
2097 QDBusMessagePrivate::setParametersValidated(msg, true); |
|
2098 msg << serviceName; |
|
2099 QDBusMessage reply = sendWithReply(msg, QDBus::Block); |
|
2100 if (reply.type() == QDBusMessage::ReplyMessage) |
|
2101 return reply.arguments().at(0).toString(); |
|
2102 return QString(); |
|
2103 } |
|
2104 |
|
2105 QDBusMetaObject * |
|
2106 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path, |
|
2107 const QString &interface, QDBusError &error) |
|
2108 { |
|
2109 // service must be a unique connection name |
|
2110 if (!interface.isEmpty()) { |
|
2111 QDBusReadLocker locker(FindMetaObject1Action, this); |
|
2112 QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0); |
|
2113 if (mo) |
|
2114 return mo; |
|
2115 } |
|
2116 |
|
2117 // introspect the target object |
|
2118 QDBusMessage msg = QDBusMessage::createMethodCall(service, path, |
|
2119 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE), |
|
2120 QLatin1String("Introspect")); |
|
2121 QDBusMessagePrivate::setParametersValidated(msg, true); |
|
2122 |
|
2123 QDBusMessage reply = sendWithReply(msg, QDBus::Block); |
|
2124 |
|
2125 // it doesn't exist yet, we have to create it |
|
2126 QDBusWriteLocker locker(FindMetaObject2Action, this); |
|
2127 QDBusMetaObject *mo = 0; |
|
2128 if (!interface.isEmpty()) |
|
2129 mo = cachedMetaObjects.value(interface, 0); |
|
2130 if (mo) |
|
2131 // maybe it got created when we switched from read to write lock |
|
2132 return mo; |
|
2133 |
|
2134 QString xml; |
|
2135 if (reply.type() == QDBusMessage::ReplyMessage) { |
|
2136 if (reply.signature() == QLatin1String("s")) |
|
2137 // fetch the XML description |
|
2138 xml = reply.arguments().at(0).toString(); |
|
2139 } else { |
|
2140 error = reply; |
|
2141 lastError = error; |
|
2142 if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod) |
|
2143 return 0; // error |
|
2144 } |
|
2145 |
|
2146 // release the lock and return |
|
2147 QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml, |
|
2148 cachedMetaObjects, error); |
|
2149 lastError = error; |
|
2150 return result; |
|
2151 } |
|
2152 |
|
2153 void QDBusConnectionPrivate::registerService(const QString &serviceName) |
|
2154 { |
|
2155 QDBusWriteLocker locker(RegisterServiceAction, this); |
|
2156 serviceNames.append(serviceName); |
|
2157 } |
|
2158 |
|
2159 void QDBusConnectionPrivate::unregisterService(const QString &serviceName) |
|
2160 { |
|
2161 QDBusWriteLocker locker(UnregisterServiceAction, this); |
|
2162 serviceNames.removeAll(serviceName); |
|
2163 } |
|
2164 |
|
2165 bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName) const |
|
2166 { |
|
2167 if (serviceName == baseService) |
|
2168 return true; |
|
2169 QStringList copy = serviceNames; |
|
2170 return copy.contains(serviceName); |
|
2171 } |
|
2172 |
|
2173 void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev) |
|
2174 { |
|
2175 QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this); |
|
2176 QCoreApplication::postEvent(object, ev); |
|
2177 QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this); |
|
2178 } |
|
2179 |
|
2180 QT_END_NAMESPACE |