1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "DBusConnection.h"
5 #include "DBusInputStream.h"
17 DBusObjectPathVTable DBusConnection::libdbusObjectPathVTable_ = {
18 NULL, // no need to handle unregister callbacks
19 &DBusConnection::onLibdbusObjectPathMessageThunk
22 void DBusConnection::dispatch() {
23 while (!stopDispatching_ && readWriteDispatch(10)) {
27 DBusConnection::DBusConnection(BusType busType) :
29 libdbusConnection_(NULL),
30 isLibdbusSignalFilterAdded_(false),
31 stopDispatching_(false) {
32 dbus_threads_init_default();
35 DBusConnection::DBusConnection(::DBusConnection* libDbusConnection) :
37 libdbusConnection_(libDbusConnection),
38 isLibdbusSignalFilterAdded_(false),
39 stopDispatching_(false) {
40 dbus_threads_init_default();
43 DBusConnection::~DBusConnection() {
47 dispatchThread_.join();
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;
84 if (!dbusSignalMatchRulesMap_.empty()) {
85 dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
88 dbus_connection_close(libdbusConnection_);
89 dbus_connection_unref(libdbusConnection_);
90 libdbusConnection_ = NULL;
92 dbusConnectionStatusEvent_.notifyListeners(AvailabilityStatus::NOT_AVAILABLE);
96 bool DBusConnection::isConnected() const {
97 return (libdbusConnection_ != NULL);
100 DBusConnectionStatusEvent& DBusConnection::getConnectionStatusEvent() {
101 return dbusConnectionStatusEvent_;
104 const std::shared_ptr<DBusServiceRegistry>& DBusConnection::getDBusServiceRegistry() {
105 if (!dbusServiceRegistry_) {
106 dbusServiceRegistry_ = std::make_shared<DBusServiceRegistry>(this->shared_from_this());
109 return dbusServiceRegistry_;
112 const std::shared_ptr<DBusDaemonProxy>& DBusConnection::getDBusDaemonProxy() {
113 if (!dbusDaemonProxy_) {
114 dbusDaemonProxy_ = std::make_shared<DBusDaemonProxy>(this->shared_from_this());
117 return dbusDaemonProxy_;
120 const std::shared_ptr<DBusObjectManager>& DBusConnection::getDBusObjectManager() {
121 if (!dbusObjectManager_) {
122 dbusObjectManager_ = std::make_shared<DBusObjectManager>(this->shared_from_this());
125 return dbusObjectManager_;
128 bool DBusConnection::requestServiceNameAndBlock(const std::string& serviceName) const {
130 const int libdbusStatus = dbus_bus_request_name(libdbusConnection_,
132 DBUS_NAME_FLAG_DO_NOT_QUEUE,
133 &dbusError.libdbusError_);
134 const bool isServiceNameAcquired = (libdbusStatus == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
136 return isServiceNameAcquired;
139 bool DBusConnection::releaseServiceName(const std::string& serviceName) const {
141 const int libdbusStatus = dbus_bus_release_name(libdbusConnection_,
143 &dbusError.libdbusError_);
144 const bool isServiceNameReleased = (libdbusStatus == DBUS_RELEASE_NAME_REPLY_RELEASED);
146 return isServiceNameReleased;
149 bool DBusConnection::sendDBusMessage(const DBusMessage& dbusMessage, uint32_t* allocatedSerial) const {
151 assert(isConnected());
153 dbus_uint32_t* libdbusSerial = static_cast<dbus_uint32_t*>(allocatedSerial);
154 const bool result = dbus_connection_send(libdbusConnection_, dbusMessage.libdbusMessage_, libdbusSerial);
159 void DBusConnection::onLibdbusPendingCallNotifyThunk(::DBusPendingCall* libdbusPendingCall, void *userData) {
161 assert(libdbusPendingCall);
163 auto dbusMessageReplyAsyncHandler = reinterpret_cast<DBusMessageReplyAsyncHandler*>(userData);
165 ::DBusMessage* libdbusMessage = dbus_pending_call_steal_reply(
167 const bool increaseLibdbusMessageReferenceCount = false;
168 DBusMessage dbusMessage(libdbusMessage, increaseLibdbusMessageReferenceCount);
170 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::SUCCESS, dbusMessage);
172 // libdbus calls the Cleanup method below
173 dbus_pending_call_unref(libdbusPendingCall);
176 void DBusConnection::onLibdbusDataCleanup(void* userData) {
177 auto dbusMessageReplyAsyncHandler = reinterpret_cast<DBusMessageReplyAsyncHandler*>(userData);
178 delete dbusMessageReplyAsyncHandler;
181 std::future<CallStatus> DBusConnection::sendDBusMessageWithReplyAsync(
182 const DBusMessage& dbusMessage,
183 std::unique_ptr<DBusMessageReplyAsyncHandler> dbusMessageReplyAsyncHandler,
184 int timeoutMilliseconds) const {
187 assert(isConnected());
189 DBusPendingCall* libdbusPendingCall;
190 dbus_bool_t libdbusSuccess;
192 libdbusSuccess = dbus_connection_send_with_reply(
194 dbusMessage.libdbusMessage_,
196 timeoutMilliseconds);
198 if (!libdbusSuccess || !libdbusPendingCall) {
199 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::CONNECTION_FAILED, dbusMessage);
200 return dbusMessageReplyAsyncHandler->getFuture();
203 libdbusSuccess = dbus_pending_call_set_notify(
205 onLibdbusPendingCallNotifyThunk,
206 dbusMessageReplyAsyncHandler.get(),
207 onLibdbusDataCleanup);
209 if (!libdbusSuccess) {
210 dbusMessageReplyAsyncHandler->onDBusMessageReply(CallStatus::OUT_OF_MEMORY, dbusMessage);
211 dbus_pending_call_unref(libdbusPendingCall);
212 return dbusMessageReplyAsyncHandler->getFuture();
215 return dbusMessageReplyAsyncHandler.release()->getFuture();
219 DBusMessage DBusConnection::sendDBusMessageWithReplyAndBlock(const DBusMessage& dbusMessage,
220 DBusError& dbusError,
221 int timeoutMilliseconds) const {
224 assert(isConnected());
226 ::DBusMessage* libdbusMessageReply = dbus_connection_send_with_reply_and_block(libdbusConnection_,
227 dbusMessage.libdbusMessage_,
229 &dbusError.libdbusError_);
231 return DBusMessage();
233 const bool increaseLibdbusMessageReferenceCount = false;
234 return DBusMessage(libdbusMessageReply, increaseLibdbusMessageReferenceCount);
238 bool DBusConnection::readWriteDispatch(int timeoutMilliseconds) {
240 const dbus_bool_t libdbusSuccess = dbus_connection_read_write_dispatch(libdbusConnection_,
241 timeoutMilliseconds);
242 return libdbusSuccess;
247 DBusProxyConnection::DBusSignalHandlerToken DBusConnection::addSignalMemberHandler(const std::string& objectPath,
248 const std::string& interfaceName,
249 const std::string& interfaceMemberName,
250 const std::string& interfaceMemberSignature,
251 DBusSignalHandler* dbusSignalHandler) {
252 DBusSignalHandlerPath dbusSignalHandlerPath(
256 interfaceMemberSignature);
257 const bool isFirstSignalMemberHandler = dbusSignalHandlerTable_.find(dbusSignalHandlerPath) == dbusSignalHandlerTable_.end();
259 dbusSignalHandlerTable_.insert(DBusSignalHandlerTable::value_type(dbusSignalHandlerPath, dbusSignalHandler));
261 if (isFirstSignalMemberHandler)
262 addLibdbusSignalMatchRule(objectPath, interfaceName, interfaceMemberName);
264 return dbusSignalHandlerPath;
267 void DBusConnection::removeSignalMemberHandler(const DBusSignalHandlerToken& dbusSignalHandlerToken) {
268 auto equalRangeIteratorPair = dbusSignalHandlerTable_.equal_range(dbusSignalHandlerToken);
270 // the range can't be empty!
271 assert(equalRangeIteratorPair.first != equalRangeIteratorPair.second);
273 // advance to the next element
274 equalRangeIteratorPair.first++;
276 // check if the first element was the only element
277 const bool isLastSignalMemberHandler = equalRangeIteratorPair.first == equalRangeIteratorPair.second;
279 if (isLastSignalMemberHandler) {
280 const std::string& objectPath = std::get<0>(dbusSignalHandlerToken);
281 const std::string& interfaceName = std::get<1>(dbusSignalHandlerToken);
282 const std::string& interfaceMemberName = std::get<2>(dbusSignalHandlerToken);
284 removeLibdbusSignalMatchRule(objectPath, interfaceName, interfaceMemberName);
287 dbusSignalHandlerTable_.erase(dbusSignalHandlerToken);
290 void DBusConnection::registerObjectPath(const std::string& objectPath) {
291 assert(!objectPath.empty());
292 assert(objectPath[0] == '/');
294 auto handlerIterator = libdbusRegisteredObjectPaths_.find(objectPath);
295 const bool foundRegisteredObjectPathHandler = handlerIterator != libdbusRegisteredObjectPaths_.end();
297 if (foundRegisteredObjectPathHandler) {
298 uint32_t& referenceCount = handlerIterator->second;
305 libdbusRegisteredObjectPaths_.insert(LibdbusRegisteredObjectPathHandlersTable::value_type(objectPath, 1));
309 const dbus_bool_t libdbusSuccess = dbus_connection_try_register_object_path(libdbusConnection_,
311 &libdbusObjectPathVTable_,
313 &dbusError.libdbusError_);
314 assert(libdbusSuccess);
319 void DBusConnection::unregisterObjectPath(const std::string& objectPath) {
320 assert(!objectPath.empty());
321 assert(objectPath[0] == '/');
323 auto handlerIterator = libdbusRegisteredObjectPaths_.find(objectPath);
324 const bool foundRegisteredObjectPathHandler = handlerIterator != libdbusRegisteredObjectPaths_.end();
326 assert(foundRegisteredObjectPathHandler);
328 uint32_t& referenceCount = handlerIterator->second;
329 if (referenceCount > 1) {
334 libdbusRegisteredObjectPaths_.erase(handlerIterator);
337 dbus_bool_t libdbusSuccess = dbus_connection_unregister_object_path(libdbusConnection_,
339 assert(libdbusSuccess);
343 void DBusConnection::addLibdbusSignalMatchRule(const std::string& objectPath,
344 const std::string& interfaceName,
345 const std::string& interfaceMemberName) {
346 DBusSignalMatchRuleTuple dbusSignalMatchRuleTuple(objectPath, interfaceName, interfaceMemberName);
347 auto matchRuleIterator = dbusSignalMatchRulesMap_.find(dbusSignalMatchRuleTuple);
348 const bool matchRuleFound = matchRuleIterator != dbusSignalMatchRulesMap_.end();
350 if (matchRuleFound) {
351 uint32_t& matchRuleReferenceCount = matchRuleIterator->second.first;
352 matchRuleReferenceCount++;
356 const bool isFirstMatchRule = dbusSignalMatchRulesMap_.empty();
358 // generate D-Bus match rule string
359 std::ostringstream matchRuleStringStream;
361 matchRuleStringStream << "type='signal'";
362 matchRuleStringStream << ",path='" << objectPath << "'";
363 matchRuleStringStream << ",interface='" << interfaceName << "'";
364 matchRuleStringStream << ",member='" << interfaceMemberName << "'";
366 // add the match rule string to the map with reference count set to 1
367 std::string matchRuleString = matchRuleStringStream.str();
368 auto success = dbusSignalMatchRulesMap_.insert(
369 DBusSignalMatchRulesMap::value_type(dbusSignalMatchRuleTuple,
370 DBusSignalMatchRuleMapping(1, matchRuleString)));
371 assert(success.second);
373 // if not connected the filter and the rules will be added as soon as the connection is established
375 // add the libdbus message signal filter
376 if (isFirstMatchRule) {
377 const dbus_bool_t libdbusSuccess = dbus_connection_add_filter(libdbusConnection_,
378 &onLibdbusSignalFilterThunk,
381 assert(libdbusSuccess);
384 // finally add the match rule
386 dbus_bus_add_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
391 void DBusConnection::removeLibdbusSignalMatchRule(const std::string& objectPath,
392 const std::string& interfaceName,
393 const std::string& interfaceMemberName) {
394 DBusSignalMatchRuleTuple dbusSignalMatchRuleTuple(objectPath, interfaceName, interfaceMemberName);
395 auto matchRuleIterator = dbusSignalMatchRulesMap_.find(dbusSignalMatchRuleTuple);
396 const bool matchRuleFound = matchRuleIterator != dbusSignalMatchRulesMap_.end();
398 assert(matchRuleFound);
400 uint32_t& matchRuleReferenceCount = matchRuleIterator->second.first;
401 if (matchRuleReferenceCount > 1) {
402 matchRuleReferenceCount--;
406 const std::string& matchRuleString = matchRuleIterator->second.second;
408 dbus_bus_remove_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
411 dbusSignalMatchRulesMap_.erase(matchRuleIterator);
413 const bool isLastMatchRule = dbusSignalMatchRulesMap_.empty();
415 dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
418 void DBusConnection::initLibdbusObjectPathHandlerAfterConnect() {
419 assert(isConnected());
421 // nothing to do if there aren't any registered object path handlers
422 if (libdbusRegisteredObjectPaths_.empty())
426 dbus_bool_t libdbusSuccess;
428 for ( auto handlerIterator = libdbusRegisteredObjectPaths_.begin();
429 handlerIterator != libdbusRegisteredObjectPaths_.end();
431 const std::string& objectPath = handlerIterator->first;
435 libdbusSuccess = dbus_connection_try_register_object_path(libdbusConnection_,
437 &libdbusObjectPathVTable_,
439 &dbusError.libdbusError_);
440 assert(libdbusSuccess);
445 void DBusConnection::initLibdbusSignalFilterAfterConnect() {
446 assert(isConnected());
448 // nothing to do if there aren't any signal match rules
449 if (dbusSignalMatchRulesMap_.empty())
452 // first we add the libdbus message signal filter
453 const dbus_bool_t libdbusSuccess = dbus_connection_add_filter(libdbusConnection_,
454 &onLibdbusSignalFilterThunk,
457 assert(libdbusSuccess);
459 // then we upload all match rules to the dbus-daemon
461 for (auto iterator = dbusSignalMatchRulesMap_.begin(); iterator != dbusSignalMatchRulesMap_.end(); iterator++) {
462 const std::string& matchRuleString = iterator->second.second;
465 dbus_bus_add_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
470 ::DBusHandlerResult DBusConnection::onLibdbusObjectPathMessage(::DBusMessage* libdbusMessage) const {
471 assert(libdbusMessage);
473 // handle only method call messages
474 if (dbus_message_get_type(libdbusMessage) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
475 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
478 bool isDBusMessageHandled = dbusObjectManager_->handleMessage(DBusMessage(libdbusMessage));
479 return isDBusMessageHandled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
482 ::DBusHandlerResult DBusConnection::onLibdbusSignalFilter(::DBusMessage* libdbusMessage) {
483 assert(libdbusMessage);
485 // handle only signal messages
486 if (dbus_message_get_type(libdbusMessage) != DBUS_MESSAGE_TYPE_SIGNAL)
487 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
489 const char* objectPath = dbus_message_get_path(libdbusMessage);
490 const char* interfaceName = dbus_message_get_interface(libdbusMessage);
491 const char* interfaceMemberName = dbus_message_get_member(libdbusMessage);
492 const char* interfaceMemberSignature = dbus_message_get_signature(libdbusMessage);
495 assert(interfaceName);
496 assert(interfaceMemberName);
497 assert(interfaceMemberSignature);
499 DBusSignalHandlerPath dbusSignalHandlerPath(objectPath, interfaceName, interfaceMemberName, interfaceMemberSignature);
500 auto equalRangeIteratorPair = dbusSignalHandlerTable_.equal_range(dbusSignalHandlerPath);
502 if (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
503 DBusMessage dbusMessage(libdbusMessage);
505 while (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
506 DBusSignalHandler* dbusSignalHandler = equalRangeIteratorPair.first->second;
507 const SubscriptionStatus dbusSignalHandlerSubscriptionStatus = dbusSignalHandler->onSignalDBusMessage(dbusMessage);
509 if (dbusSignalHandlerSubscriptionStatus == SubscriptionStatus::CANCEL) {
510 auto dbusSignalHandlerSubscription = equalRangeIteratorPair.first;
511 equalRangeIteratorPair.first++;
512 dbusSignalHandlerTable_.erase(dbusSignalHandlerSubscription);
514 equalRangeIteratorPair.first++;
517 return DBUS_HANDLER_RESULT_HANDLED;
520 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
523 ::DBusHandlerResult DBusConnection::onLibdbusSignalFilterThunk(::DBusConnection* libdbusConnection,
524 ::DBusMessage* libdbusMessage,
526 assert(libdbusConnection);
527 assert(libdbusMessage);
530 DBusConnection* dbusConnection = reinterpret_cast<DBusConnection*>(userData);
532 assert(dbusConnection->libdbusConnection_ == libdbusConnection);
534 return dbusConnection->onLibdbusSignalFilter(libdbusMessage);
537 ::DBusHandlerResult DBusConnection::onLibdbusObjectPathMessageThunk(::DBusConnection* libdbusConnection,
538 ::DBusMessage* libdbusMessage,
540 assert(libdbusConnection);
541 assert(libdbusMessage);
544 const DBusConnection* dbusConnection = reinterpret_cast<DBusConnection*>(userData);
546 assert(dbusConnection->libdbusConnection_ == libdbusConnection);
548 return dbusConnection->onLibdbusObjectPathMessage(libdbusMessage);
552 } // namespace CommonAPI