1 /* Copyright (C) 2013 BMW Group
2 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
3 * Author: Juergen Gehring (juergen.gehring@bmw.de)
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DBusConnection.h"
8 #include "DBusInputStream.h"
18 DBusObjectPathVTable DBusConnection::libdbusObjectPathVTable_ = {
19 NULL, // no need to handle unregister callbacks
20 &DBusConnection::onLibdbusObjectPathMessageThunk
23 void DBusConnection::dispatch() {
24 while (!stopDispatching_ && readWriteDispatch(10)) {
28 DBusConnection::DBusConnection(BusType busType) :
30 libdbusConnection_(NULL),
31 isLibdbusSignalFilterAdded_(false),
32 stopDispatching_(false) {
33 dbus_threads_init_default();
36 DBusConnection::DBusConnection(::DBusConnection* libDbusConnection) :
38 libdbusConnection_(libDbusConnection),
39 isLibdbusSignalFilterAdded_(false),
40 stopDispatching_(false) {
41 dbus_threads_init_default();
44 DBusConnection::~DBusConnection() {
50 bool DBusConnection::connect() {
52 return connect(dbusError);
55 bool DBusConnection::connect(DBusError& dbusError) {
61 const ::DBusBusType libdbusType = static_cast<DBusBusType>(busType_);
63 libdbusConnection_ = dbus_bus_get_private(libdbusType, &dbusError.libdbusError_);
67 assert(libdbusConnection_);
68 dbus_connection_set_exit_on_disconnect(libdbusConnection_, false);
70 dbusConnectionStatusEvent_.notifyListeners(AvailabilityStatus::AVAILABLE);
72 initLibdbusObjectPathHandlerAfterConnect();
74 initLibdbusSignalFilterAfterConnect();
76 dispatchThread_ = std::thread(std::bind(&DBusConnection::dispatch, this));
81 void DBusConnection::disconnect() {
83 stopDispatching_ = true;
85 if (!dbusSignalMatchRulesMap_.empty()) {
86 dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
89 dispatchThread_.join();
91 dbus_connection_close(libdbusConnection_);
92 dbus_connection_unref(libdbusConnection_);
93 libdbusConnection_ = NULL;
95 dbusConnectionStatusEvent_.notifyListeners(AvailabilityStatus::NOT_AVAILABLE);
99 bool DBusConnection::isConnected() const {
100 return (libdbusConnection_ != NULL);
103 DBusConnectionStatusEvent& DBusConnection::getConnectionStatusEvent() {
104 return dbusConnectionStatusEvent_;
107 const std::shared_ptr<DBusServiceRegistry>& DBusConnection::getDBusServiceRegistry() {
108 if (!dbusServiceRegistry_) {
109 dbusServiceRegistry_ = std::make_shared<DBusServiceRegistry>(this->shared_from_this());
112 return dbusServiceRegistry_;
115 const std::shared_ptr<DBusDaemonProxy>& DBusConnection::getDBusDaemonProxy() {
116 if (!dbusDaemonProxy_) {
117 dbusDaemonProxy_ = std::make_shared<DBusDaemonProxy>(this->shared_from_this());
120 return dbusDaemonProxy_;
123 const std::shared_ptr<DBusObjectManager>& DBusConnection::getDBusObjectManager() {
124 if (!dbusObjectManager_) {
125 dbusObjectManager_ = std::make_shared<DBusObjectManager>(this->shared_from_this());
128 return dbusObjectManager_;
131 bool DBusConnection::requestServiceNameAndBlock(const std::string& serviceName) const {
133 const int libdbusStatus = dbus_bus_request_name(libdbusConnection_,
135 DBUS_NAME_FLAG_DO_NOT_QUEUE,
136 &dbusError.libdbusError_);
137 const bool isServiceNameAcquired = (libdbusStatus == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
139 return isServiceNameAcquired;
142 bool DBusConnection::releaseServiceName(const std::string& serviceName) const {
144 const int libdbusStatus = dbus_bus_release_name(libdbusConnection_,
146 &dbusError.libdbusError_);
147 const bool isServiceNameReleased = (libdbusStatus == DBUS_RELEASE_NAME_REPLY_RELEASED);
149 return isServiceNameReleased;
152 bool DBusConnection::sendDBusMessage(const DBusMessage& dbusMessage, uint32_t* allocatedSerial) const {
154 assert(isConnected());
156 dbus_uint32_t* libdbusSerial = static_cast<dbus_uint32_t*>(allocatedSerial);
157 const bool result = dbus_connection_send(libdbusConnection_, dbusMessage.libdbusMessage_, libdbusSerial);
162 void DBusConnection::onLibdbusPendingCallNotifyThunk(::DBusPendingCall* libdbusPendingCall, void *userData) {
164 assert(libdbusPendingCall);
166 auto dbusMessageReplyAsyncHandler = reinterpret_cast<DBusMessageReplyAsyncHandler*>(userData);
168 ::DBusMessage* libdbusMessage = dbus_pending_call_steal_reply(
170 const bool increaseLibdbusMessageReferenceCount = false;
171 DBusMessage dbusMessage(libdbusMessage, increaseLibdbusMessageReferenceCount);
173 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::SUCCESS, dbusMessage);
175 // libdbus calls the Cleanup method below
176 dbus_pending_call_unref(libdbusPendingCall);
179 void DBusConnection::onLibdbusDataCleanup(void* userData) {
180 auto dbusMessageReplyAsyncHandler = reinterpret_cast<DBusMessageReplyAsyncHandler*>(userData);
181 delete dbusMessageReplyAsyncHandler;
184 std::future<CallStatus> DBusConnection::sendDBusMessageWithReplyAsync(
185 const DBusMessage& dbusMessage,
186 std::unique_ptr<DBusMessageReplyAsyncHandler> dbusMessageReplyAsyncHandler,
187 int timeoutMilliseconds) const {
190 assert(isConnected());
192 DBusPendingCall* libdbusPendingCall;
193 dbus_bool_t libdbusSuccess;
195 libdbusSuccess = dbus_connection_send_with_reply(
197 dbusMessage.libdbusMessage_,
199 timeoutMilliseconds);
201 if (!libdbusSuccess || !libdbusPendingCall) {
202 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::CONNECTION_FAILED, dbusMessage);
203 return dbusMessageReplyAsyncHandler->getFuture();
206 libdbusSuccess = dbus_pending_call_set_notify(
208 onLibdbusPendingCallNotifyThunk,
209 dbusMessageReplyAsyncHandler.get(),
210 onLibdbusDataCleanup);
212 if (!libdbusSuccess) {
213 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::OUT_OF_MEMORY, dbusMessage);
214 dbus_pending_call_unref(libdbusPendingCall);
215 return dbusMessageReplyAsyncHandler->getFuture();
218 return dbusMessageReplyAsyncHandler.release()->getFuture();
222 DBusMessage DBusConnection::sendDBusMessageWithReplyAndBlock(const DBusMessage& dbusMessage,
223 DBusError& dbusError,
224 int timeoutMilliseconds) const {
227 assert(isConnected());
229 ::DBusMessage* libdbusMessageReply = dbus_connection_send_with_reply_and_block(libdbusConnection_,
230 dbusMessage.libdbusMessage_,
232 &dbusError.libdbusError_);
234 return DBusMessage();
236 const bool increaseLibdbusMessageReferenceCount = false;
237 return DBusMessage(libdbusMessageReply, increaseLibdbusMessageReferenceCount);
241 bool DBusConnection::readWriteDispatch(int timeoutMilliseconds) {
243 const dbus_bool_t libdbusSuccess = dbus_connection_read_write_dispatch(libdbusConnection_,
244 timeoutMilliseconds);
245 return libdbusSuccess;
250 DBusProxyConnection::DBusSignalHandlerToken DBusConnection::addSignalMemberHandler(const std::string& objectPath,
251 const std::string& interfaceName,
252 const std::string& interfaceMemberName,
253 const std::string& interfaceMemberSignature,
254 DBusSignalHandler* dbusSignalHandler) {
255 DBusSignalHandlerPath dbusSignalHandlerPath(
259 interfaceMemberSignature);
260 const bool isFirstSignalMemberHandler = dbusSignalHandlerTable_.find(dbusSignalHandlerPath) == dbusSignalHandlerTable_.end();
262 dbusSignalHandlerTable_.insert(DBusSignalHandlerTable::value_type(dbusSignalHandlerPath, dbusSignalHandler));
264 if (isFirstSignalMemberHandler)
265 addLibdbusSignalMatchRule(objectPath, interfaceName, interfaceMemberName);
267 return dbusSignalHandlerPath;
270 void DBusConnection::removeSignalMemberHandler(const DBusSignalHandlerToken& dbusSignalHandlerToken) {
271 auto equalRangeIteratorPair = dbusSignalHandlerTable_.equal_range(dbusSignalHandlerToken);
273 // the range can't be empty!
274 assert(equalRangeIteratorPair.first != equalRangeIteratorPair.second);
276 // advance to the next element
277 equalRangeIteratorPair.first++;
279 // check if the first element was the only element
280 const bool isLastSignalMemberHandler = equalRangeIteratorPair.first == equalRangeIteratorPair.second;
282 if (isLastSignalMemberHandler) {
283 const std::string& objectPath = std::get<0>(dbusSignalHandlerToken);
284 const std::string& interfaceName = std::get<1>(dbusSignalHandlerToken);
285 const std::string& interfaceMemberName = std::get<2>(dbusSignalHandlerToken);
287 removeLibdbusSignalMatchRule(objectPath, interfaceName, interfaceMemberName);
290 dbusSignalHandlerTable_.erase(dbusSignalHandlerToken);
293 void DBusConnection::registerObjectPath(const std::string& objectPath) {
294 assert(!objectPath.empty());
295 assert(objectPath[0] == '/');
297 auto handlerIterator = libdbusRegisteredObjectPaths_.find(objectPath);
298 const bool foundRegisteredObjectPathHandler = handlerIterator != libdbusRegisteredObjectPaths_.end();
300 if (foundRegisteredObjectPathHandler) {
301 uint32_t& referenceCount = handlerIterator->second;
308 libdbusRegisteredObjectPaths_.insert(LibdbusRegisteredObjectPathHandlersTable::value_type(objectPath, 1));
312 const dbus_bool_t libdbusSuccess = dbus_connection_try_register_object_path(libdbusConnection_,
314 &libdbusObjectPathVTable_,
316 &dbusError.libdbusError_);
317 assert(libdbusSuccess);
322 void DBusConnection::unregisterObjectPath(const std::string& objectPath) {
323 assert(!objectPath.empty());
324 assert(objectPath[0] == '/');
326 auto handlerIterator = libdbusRegisteredObjectPaths_.find(objectPath);
327 const bool foundRegisteredObjectPathHandler = handlerIterator != libdbusRegisteredObjectPaths_.end();
329 assert(foundRegisteredObjectPathHandler);
331 uint32_t& referenceCount = handlerIterator->second;
332 if (referenceCount > 1) {
337 libdbusRegisteredObjectPaths_.erase(handlerIterator);
340 dbus_bool_t libdbusSuccess = dbus_connection_unregister_object_path(libdbusConnection_,
342 assert(libdbusSuccess);
346 void DBusConnection::addLibdbusSignalMatchRule(const std::string& objectPath,
347 const std::string& interfaceName,
348 const std::string& interfaceMemberName) {
349 DBusSignalMatchRuleTuple dbusSignalMatchRuleTuple(objectPath, interfaceName, interfaceMemberName);
350 auto matchRuleIterator = dbusSignalMatchRulesMap_.find(dbusSignalMatchRuleTuple);
351 const bool matchRuleFound = matchRuleIterator != dbusSignalMatchRulesMap_.end();
353 if (matchRuleFound) {
354 uint32_t& matchRuleReferenceCount = matchRuleIterator->second.first;
355 matchRuleReferenceCount++;
359 const bool isFirstMatchRule = dbusSignalMatchRulesMap_.empty();
361 // generate D-Bus match rule string
362 std::ostringstream matchRuleStringStream;
364 matchRuleStringStream << "type='signal'";
365 matchRuleStringStream << ",path='" << objectPath << "'";
366 matchRuleStringStream << ",interface='" << interfaceName << "'";
367 matchRuleStringStream << ",member='" << interfaceMemberName << "'";
369 // add the match rule string to the map with reference count set to 1
370 std::string matchRuleString = matchRuleStringStream.str();
371 auto success = dbusSignalMatchRulesMap_.insert(
372 DBusSignalMatchRulesMap::value_type(dbusSignalMatchRuleTuple,
373 DBusSignalMatchRuleMapping(1, matchRuleString)));
374 assert(success.second);
376 // if not connected the filter and the rules will be added as soon as the connection is established
378 // add the libdbus message signal filter
379 if (isFirstMatchRule) {
380 const dbus_bool_t libdbusSuccess = dbus_connection_add_filter(libdbusConnection_,
381 &onLibdbusSignalFilterThunk,
384 assert(libdbusSuccess);
387 // finally add the match rule
389 dbus_bus_add_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
394 void DBusConnection::removeLibdbusSignalMatchRule(const std::string& objectPath,
395 const std::string& interfaceName,
396 const std::string& interfaceMemberName) {
397 DBusSignalMatchRuleTuple dbusSignalMatchRuleTuple(objectPath, interfaceName, interfaceMemberName);
398 auto matchRuleIterator = dbusSignalMatchRulesMap_.find(dbusSignalMatchRuleTuple);
399 const bool matchRuleFound = matchRuleIterator != dbusSignalMatchRulesMap_.end();
401 assert(matchRuleFound);
403 uint32_t& matchRuleReferenceCount = matchRuleIterator->second.first;
404 if (matchRuleReferenceCount > 1) {
405 matchRuleReferenceCount--;
409 const std::string& matchRuleString = matchRuleIterator->second.second;
411 dbus_bus_remove_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
414 dbusSignalMatchRulesMap_.erase(matchRuleIterator);
416 const bool isLastMatchRule = dbusSignalMatchRulesMap_.empty();
418 dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
421 void DBusConnection::initLibdbusObjectPathHandlerAfterConnect() {
422 assert(isConnected());
424 // nothing to do if there aren't any registered object path handlers
425 if (libdbusRegisteredObjectPaths_.empty())
429 dbus_bool_t libdbusSuccess;
431 for ( auto handlerIterator = libdbusRegisteredObjectPaths_.begin();
432 handlerIterator != libdbusRegisteredObjectPaths_.end();
434 const std::string& objectPath = handlerIterator->first;
438 libdbusSuccess = dbus_connection_try_register_object_path(libdbusConnection_,
440 &libdbusObjectPathVTable_,
442 &dbusError.libdbusError_);
443 assert(libdbusSuccess);
448 void DBusConnection::initLibdbusSignalFilterAfterConnect() {
449 assert(isConnected());
451 // nothing to do if there aren't any signal match rules
452 if (dbusSignalMatchRulesMap_.empty())
455 // first we add the libdbus message signal filter
456 const dbus_bool_t libdbusSuccess = dbus_connection_add_filter(libdbusConnection_,
457 &onLibdbusSignalFilterThunk,
460 assert(libdbusSuccess);
462 // then we upload all match rules to the dbus-daemon
464 for (auto iterator = dbusSignalMatchRulesMap_.begin(); iterator != dbusSignalMatchRulesMap_.end(); iterator++) {
465 const std::string& matchRuleString = iterator->second.second;
468 dbus_bus_add_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
473 ::DBusHandlerResult DBusConnection::onLibdbusObjectPathMessage(::DBusMessage* libdbusMessage) const {
474 assert(libdbusMessage);
476 // handle only method call messages
477 if (dbus_message_get_type(libdbusMessage) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
478 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
481 bool isDBusMessageHandled = dbusObjectManager_->handleMessage(DBusMessage(libdbusMessage));
482 return isDBusMessageHandled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
485 ::DBusHandlerResult DBusConnection::onLibdbusSignalFilter(::DBusMessage* libdbusMessage) {
486 assert(libdbusMessage);
488 // handle only signal messages
489 if (dbus_message_get_type(libdbusMessage) != DBUS_MESSAGE_TYPE_SIGNAL)
490 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
492 const char* objectPath = dbus_message_get_path(libdbusMessage);
493 const char* interfaceName = dbus_message_get_interface(libdbusMessage);
494 const char* interfaceMemberName = dbus_message_get_member(libdbusMessage);
495 const char* interfaceMemberSignature = dbus_message_get_signature(libdbusMessage);
498 assert(interfaceName);
499 assert(interfaceMemberName);
500 assert(interfaceMemberSignature);
502 DBusSignalHandlerPath dbusSignalHandlerPath(objectPath, interfaceName, interfaceMemberName, interfaceMemberSignature);
503 auto equalRangeIteratorPair = dbusSignalHandlerTable_.equal_range(dbusSignalHandlerPath);
505 if (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
506 DBusMessage dbusMessage(libdbusMessage);
508 while (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
509 DBusSignalHandler* dbusSignalHandler = equalRangeIteratorPair.first->second;
510 const SubscriptionStatus dbusSignalHandlerSubscriptionStatus = dbusSignalHandler->onSignalDBusMessage(dbusMessage);
512 if (dbusSignalHandlerSubscriptionStatus == SubscriptionStatus::CANCEL) {
513 auto dbusSignalHandlerSubscription = equalRangeIteratorPair.first;
514 equalRangeIteratorPair.first++;
515 dbusSignalHandlerTable_.erase(dbusSignalHandlerSubscription);
517 equalRangeIteratorPair.first++;
520 return DBUS_HANDLER_RESULT_HANDLED;
523 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
526 ::DBusHandlerResult DBusConnection::onLibdbusSignalFilterThunk(::DBusConnection* libdbusConnection,
527 ::DBusMessage* libdbusMessage,
529 assert(libdbusConnection);
530 assert(libdbusMessage);
533 DBusConnection* dbusConnection = reinterpret_cast<DBusConnection*>(userData);
535 assert(dbusConnection->libdbusConnection_ == libdbusConnection);
537 return dbusConnection->onLibdbusSignalFilter(libdbusMessage);
540 ::DBusHandlerResult DBusConnection::onLibdbusObjectPathMessageThunk(::DBusConnection* libdbusConnection,
541 ::DBusMessage* libdbusMessage,
543 assert(libdbusConnection);
544 assert(libdbusMessage);
547 const DBusConnection* dbusConnection = reinterpret_cast<DBusConnection*>(userData);
549 assert(dbusConnection->libdbusConnection_ == libdbusConnection);
551 return dbusConnection->onLibdbusObjectPathMessage(libdbusMessage);
555 } // namespace CommonAPI