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 "DBusFunctionalHash.h"
8 #include "DBusServiceRegistry.h"
9 #include "DBusDaemonProxy.h"
10 #include "DBusProxyAsyncCallbackHandler.h"
16 DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection):
17 dbusDaemonProxy_(std::make_shared<CommonAPI::DBus::DBusDaemonProxy>(dbusProxyConnection)),
18 dbusServicesStatus_(AvailabilityStatus::UNKNOWN) {
21 DBusServiceRegistry::~DBusServiceRegistry() {
22 dbusDaemonProxy_->getNameOwnerChangedEvent().unsubscribe(dbusDaemonProxyNameOwnerChangedEventSubscription_);
23 dbusDaemonProxy_->getProxyStatusEvent().unsubscribe(dbusDaemonProxyStatusEventSubscription_);
24 std::cout << "Crushing stuff" << std::endl;
27 void DBusServiceRegistry::init() {
28 dbusDaemonProxyStatusEventSubscription_ =
29 dbusDaemonProxy_->getProxyStatusEvent().subscribeCancellableListener(
30 std::bind(&DBusServiceRegistry::onDBusDaemonProxyStatusEvent, this->shared_from_this(), std::placeholders::_1));
32 dbusDaemonProxyNameOwnerChangedEventSubscription_ =
33 dbusDaemonProxy_->getNameOwnerChangedEvent().subscribeCancellableListener(
34 std::bind(&DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent,
35 this->shared_from_this(),
36 std::placeholders::_1,
37 std::placeholders::_2,
38 std::placeholders::_3));
41 bool DBusServiceRegistry::waitDBusServicesAvailable(std::unique_lock<std::mutex>& lock, std::chrono::milliseconds& timeout) {
42 bool dbusServicesStatusIsKnown = (dbusServicesStatus_ != AvailabilityStatus::UNKNOWN);
44 while (!dbusServicesStatusIsKnown && timeout.count() > 0) {
45 typedef std::chrono::high_resolution_clock clock;
46 clock::time_point startTimePoint = clock::now();
48 dbusServicesStatusIsKnown = dbusServiceChanged_.wait_for(
51 [&]{ return dbusServicesStatus_ != AvailabilityStatus::UNKNOWN; });
53 std::chrono::milliseconds elapsedWaitTime =
54 std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - startTimePoint);
56 if (elapsedWaitTime > timeout) {
57 timeout = std::chrono::milliseconds::zero();
61 timeout -= elapsedWaitTime;
64 return (dbusServicesStatus_ == AvailabilityStatus::AVAILABLE);
67 bool DBusServiceRegistry::isServiceInstanceAlive(const std::string& dbusInterfaceName, const std::string& dbusServiceName, const std::string& dbusObjectPath) {
68 if (!dbusDaemonProxy_->isAvailable()) {
72 std::chrono::milliseconds timeout(2000);
73 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
75 if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
79 auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
80 if (dbusServiceIterator == dbusServices_.end()) {
84 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
86 if (dbusServiceState == DBusServiceState::AVAILABLE) {
87 resolveDBusServiceInstances(dbusServiceIterator);
90 if (dbusServiceState == DBusServiceState::RESOLVING) {
91 dbusServiceChanged_.wait_for(
94 [&] { return dbusServiceState != DBusServiceState::RESOLVING; });
97 const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
98 auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
100 if (dbusInstanceIterator != dbusInstanceList.end()) {
101 const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
103 return (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE);
109 // Go through the list of available services and check their interface lists
110 // If a list is still unknown, then send request to the remote object manager and count it as invalid
111 // If a list is in acquiring state, then just count it as invalid and skip over it
112 // Add all matching valid services to the available service list
113 // If the invalid service count is set, then wait upto waitTimeLimit (2 seconds) for the object manager requests to complete
114 // If the timeout expires, then go through the list for last time and add everything matching
115 // If the timeout didn't expire, then go through the list again and send requests for new UNKNOWN services, then wait again for them to complete
116 // Known limitations:
117 // - if the method is called before the first "listNames()" call completes, this request will be blocked
118 // - if libdbus is broken and doesn't report errors to timed out requests, then this request will always block for the default 2 seconds (waitTimeLimit)
119 // - the method has to be called many times, if you actually want to wait for all services, otherwise you'll always get a partial response. I.e. the more you call this method, the hotter the internal cache gets.
120 std::vector<std::string> DBusServiceRegistry::getAvailableServiceInstances(const std::string& serviceName,
121 const std::string& domainName) {
122 std::vector<std::string> availableServiceInstances;
124 if (!dbusDaemonProxy_->isAvailable()) {
125 return availableServiceInstances;
128 std::chrono::milliseconds timeout(2000);
129 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
131 if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
132 return availableServiceInstances;
135 while (timeout.count() > 0) {
136 size_t dbusServiceResolvingCount = getAvailableServiceInstances(serviceName, availableServiceInstances);
138 if (!dbusServiceResolvingCount) {
142 // wait for unknown and acquiring services, then restart from the beginning
143 typedef std::chrono::high_resolution_clock clock;
144 clock::time_point startTimePoint = clock::now();
146 size_t wakeupCount = 0;
147 dbusServiceChanged_.wait_for(
152 return wakeupCount > dbusServiceResolvingCount;
155 if (wakeupCount > 1) {
156 getAvailableServiceInstances(serviceName, availableServiceInstances);
160 std::chrono::milliseconds elapsedWaitTime =
161 std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - startTimePoint);
162 timeout -= elapsedWaitTime;
165 // maybe partial list but it contains everything we know for now
166 return availableServiceInstances;
169 size_t DBusServiceRegistry::getAvailableServiceInstances(const std::string& dbusInterfaceName, std::vector<std::string>& availableServiceInstances) {
170 size_t dbusServicesResolvingCount = 0;
172 availableServiceInstances.clear();
174 // caller must hold lock
175 auto dbusServiceIterator = dbusServices_.begin();
176 while (dbusServiceIterator != dbusServices_.end()) {
177 const std::string& dbusServiceName = dbusServiceIterator->first;
178 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
179 const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
181 // count the resolving services and start aclquiring the objects for unknown ones
182 switch (dbusServiceState) {
183 case DBusServiceState::AVAILABLE:
184 resolveDBusServiceInstances(dbusServiceIterator);
185 dbusServicesResolvingCount++;
188 case DBusServiceState::RESOLVING:
189 case DBusServiceState::RESOLVED:
190 if (dbusServiceState == DBusServiceState::RESOLVING) {
191 dbusServicesResolvingCount++;
194 for (auto& dbusInstanceIterator : dbusInstanceList) {
195 const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
196 const std::string& dbusInstanceObjectPath = dbusInstanceIterator.first.first;
197 const std::string& dbusInstanceInterfaceName = dbusInstanceIterator.first.second;
199 if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE
200 && dbusInstanceInterfaceName == dbusInterfaceName) {
201 std::string commonApiAddress;
203 DBusAddressTranslator::getInstance().searchForCommonAddress(
206 dbusInstanceObjectPath,
209 availableServiceInstances.emplace_back(std::move(commonApiAddress));
215 dbusServiceIterator++;
218 return dbusServicesResolvingCount;
222 DBusServiceRegistry::Subscription DBusServiceRegistry::subscribeAvailabilityListener(const std::string& commonApiAddress,
223 DBusServiceListener serviceListener) {
224 std::string dbusInterfaceName;
225 std::string dbusServiceName;
226 std::string dbusObjectPath;
228 DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
230 std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
232 auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
234 // add service for the first time
235 if (dbusServiceIterator == dbusServices_.end()) {
236 DBusServiceState dbusServiceState = DBusServiceState::UNKNOWN;
238 if (dbusServicesStatus_ == AvailabilityStatus::AVAILABLE) {
239 dbusServiceState = DBusServiceState::NOT_AVAILABLE;
242 auto insertIterator = dbusServices_.insert({ dbusServiceName, { dbusServiceState, DBusInstanceList() } });
243 assert(insertIterator.second);
244 dbusServiceIterator = insertIterator.first;
247 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
248 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
250 auto dbusInstanceIterator = addDBusServiceInstance(
254 AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
255 DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
257 if (dbusServiceState == DBusServiceState::RESOLVED
258 && dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
259 dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
262 Subscription listenerSubscription = dbusServiceListenerList.insert(
263 dbusServiceListenerList.end(), serviceListener);
265 switch (dbusServiceState) {
266 case DBusServiceState::AVAILABLE:
267 resolveDBusServiceInstances(dbusServiceIterator);
270 case DBusServiceState::RESOLVING:
271 if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE) {
272 serviceListener(dbusInstanceAvailabilityStatus);
276 case DBusServiceState::RESOLVED:
277 case DBusServiceState::NOT_AVAILABLE:
278 serviceListener(dbusInstanceAvailabilityStatus);
282 return listenerSubscription;
285 void DBusServiceRegistry::unsubscribeAvailabilityListener(const std::string& commonApiAddress,
286 Subscription& listenerSubscription) {
287 std::string dbusInterfaceName;
288 std::string dbusServiceName;
289 std::string dbusObjectPath;
291 DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
293 std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
294 auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
296 if (dbusServiceIterator == dbusServices_.end()) {
300 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
301 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
303 auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
304 if (dbusInstanceIterator == dbusInstanceList.end()) {
308 const AvailabilityStatus& dbusServiceAvailabilityStatus = dbusInstanceIterator->second.first;
309 DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
311 dbusServiceListenerList.erase(listenerSubscription);
313 if (dbusServiceListenerList.empty() && dbusServiceAvailabilityStatus != AvailabilityStatus::AVAILABLE) {
314 dbusInstanceList.erase(dbusInstanceIterator);
316 if (dbusInstanceList.empty() && dbusServiceState == DBusServiceState::UNKNOWN) {
317 dbusServices_.erase(dbusServiceIterator);
322 SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus) {
323 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
325 switch (availabilityStatus) {
326 case AvailabilityStatus::AVAILABLE:
327 dbusServicesStatus_ = AvailabilityStatus::UNKNOWN;
328 dbusDaemonProxy_->listNamesAsync(std::bind(
329 &DBusServiceRegistry::onListNamesCallback,
330 this->shared_from_this(),
331 std::placeholders::_1,
332 std::placeholders::_2));
335 case AvailabilityStatus::NOT_AVAILABLE:
336 auto dbusServiceIterator = dbusServices_.begin();
338 while (dbusServiceIterator != dbusServices_.end()) {
339 dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
342 dbusServicesStatus_ = AvailabilityStatus::NOT_AVAILABLE;
346 return SubscriptionStatus::RETAIN;
349 SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent(const std::string& affectedName,
350 const std::string& oldOwner,
351 const std::string& newOwner) {
352 if (isDBusServiceName(affectedName)) {
353 AvailabilityStatus dbusServiceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
355 if (newOwner.empty()) {
356 dbusServiceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
359 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
361 onDBusServiceAvailabilityStatus(affectedName, dbusServiceAvailabilityStatus);
364 return SubscriptionStatus::RETAIN;
367 void DBusServiceRegistry::onListNamesCallback(const CommonAPI::CallStatus& callStatus, std::vector<std::string> dbusNames) {
368 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
370 if (callStatus == CallStatus::SUCCESS) {
371 for (const std::string& dbusName : dbusNames) {
372 if (isDBusServiceName(dbusName)) {
373 onDBusServiceAvailabilityStatus(dbusName, AvailabilityStatus::AVAILABLE);
378 dbusServicesStatus_ = AvailabilityStatus::AVAILABLE;
380 auto dbusServiceIterator = dbusServices_.begin();
381 while (dbusServiceIterator != dbusServices_.end()) {
382 const DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
384 if (dbusServiceState == DBusServiceState::UNKNOWN) {
385 dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
387 dbusServiceIterator++;
392 void DBusServiceRegistry::onDBusServiceAvailabilityStatus(const std::string& dbusServiceName, const AvailabilityStatus& availabilityStatus) {
393 auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
395 if (dbusServiceIterator != dbusServices_.end()) {
396 onDBusServiceAvailabilityStatus(dbusServiceIterator, availabilityStatus);
398 } else if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
399 dbusServices_.insert({ dbusServiceName, { DBusServiceState::AVAILABLE, DBusInstanceList() } });
400 dbusServiceChanged_.notify_all();
404 DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceAvailabilityStatus(DBusServiceList::iterator& dbusServiceIterator,
405 const AvailabilityStatus& availabilityStatus) {
406 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
407 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
409 if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
410 const std::string& dbusServiceName = dbusServiceIterator->first;
412 if (dbusServiceState != DBusServiceState::RESOLVING) {
413 resolveDBusServiceInstances(dbusServiceIterator);
416 return dbusServiceIterator;
419 dbusServiceState = (availabilityStatus == AvailabilityStatus::UNKNOWN) ?
420 DBusServiceState::UNKNOWN :
421 DBusServiceState::NOT_AVAILABLE;
423 return onDBusServiceOffline(dbusServiceIterator, dbusServiceState);
426 DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceOffline(DBusServiceList::iterator& dbusServiceIterator,
427 const DBusServiceState& newDBusServiceState) {
428 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
429 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
430 auto dbusInstanceIterator = dbusInstanceList.begin();
432 assert(newDBusServiceState == DBusServiceState::UNKNOWN || newDBusServiceState == DBusServiceState::NOT_AVAILABLE);
434 dbusServiceState = newDBusServiceState;
436 while (dbusInstanceIterator != dbusInstanceList.end()) {
437 AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
438 DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
441 if (!dbusServiceListenerList.empty()) {
442 // the internal state is unknown until the next time we ask the object manager
443 notifyDBusServiceListeners(dbusServiceListenerList, AvailabilityStatus::NOT_AVAILABLE);
444 dbusInstanceAvailabilityStatus = AvailabilityStatus::UNKNOWN;
445 dbusInstanceIterator++;
447 dbusInstanceIterator = dbusInstanceList.erase(dbusInstanceIterator);
451 dbusServiceChanged_.notify_all();
453 if (dbusInstanceList.empty()) {
454 return dbusServices_.erase(dbusServiceIterator);
457 dbusServiceIterator++;
459 return dbusServiceIterator;
462 void DBusServiceRegistry::resolveDBusServiceInstances(DBusServiceList::iterator& dbusServiceIterator) {
463 const std::string& dbusServiceName = dbusServiceIterator->first;
464 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
465 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
466 std::vector<DBusServiceAddress> predefinedDBusServiceInstances;
468 dbusServiceState = DBusServiceState::RESOLVING;
470 // add predefined instances
471 DBusAddressTranslator::getInstance().getPredefinedInstances(dbusServiceName, predefinedDBusServiceInstances);
473 for (auto& dbusServiceAddress : predefinedDBusServiceInstances) {
474 const std::string& dbusObjectPath = std::get<1>(dbusServiceAddress);
475 const std::string& dbusInterfaceName = std::get<2>(dbusServiceAddress);
477 onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
480 dbusServiceChanged_.notify_all();
482 // search for remote instances
483 DBusDaemonProxy::GetManagedObjectsAsyncCallback callback = std::bind(&DBusServiceRegistry::onGetManagedObjectsCallback,
484 this->shared_from_this(),
485 std::placeholders::_1,
486 std::placeholders::_2,
488 dbusDaemonProxy_->getManagedObjectsAsync(dbusServiceName, callback);
491 void DBusServiceRegistry::onGetManagedObjectsCallback(const CallStatus& callStatus,
492 DBusDaemonProxy::DBusObjectToInterfaceDict managedObjects,
493 const std::string& dbusServiceName) {
494 std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
497 if (dbusServicesStatus_ == AvailabilityStatus::NOT_AVAILABLE) {
501 auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
502 if (dbusServiceIterator == dbusServices_.end()) {
503 return; // nothing we can do
506 DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
507 DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
509 dbusServiceState = DBusServiceState::RESOLVED;
511 if (callStatus == CallStatus::SUCCESS) {
512 for (auto& dbusObjectPathIterator : managedObjects) {
513 const std::string& dbusObjectPath = dbusObjectPathIterator.first;
515 for (auto& dbusInterfaceNameIterator : dbusObjectPathIterator.second) {
516 const std::string& dbusInterfaceName = dbusInterfaceNameIterator.first;
518 onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
523 dbusServiceChanged_.notify_all();
525 // notify only UNKNOWN. The predefined and resolved have already been handled
526 for (auto& dbusInstanceIterator : dbusInstanceList) {
527 AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
528 DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator.second.second;
530 if (dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
531 dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
532 notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
537 void DBusServiceRegistry::onDBusServiceInstanceAvailable(DBusInstanceList& dbusInstanceList,
538 const std::string& dbusObjectPath,
539 const std::string& dbusInterfaceName) {
540 auto dbusInstanceIterator = addDBusServiceInstance(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
541 AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
542 DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
544 dbusInstanceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
546 notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
549 DBusServiceRegistry::DBusInstanceList::iterator DBusServiceRegistry::addDBusServiceInstance(DBusInstanceList& dbusInstanceList,
550 const std::string& dbusObjectPath,
551 const std::string& dbusInterfaceName) {
552 auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
554 // add instance for the first time
555 if (dbusInstanceIterator == dbusInstanceList.end()) {
556 auto insertIterator = dbusInstanceList.insert(
557 { { dbusObjectPath, dbusInterfaceName }, { AvailabilityStatus::UNKNOWN, DBusServiceListenerList() } });
558 const bool& insertSuccessfull = insertIterator.second;
560 assert(insertSuccessfull);
561 dbusInstanceIterator = insertIterator.first;
564 return dbusInstanceIterator;
567 void DBusServiceRegistry::notifyDBusServiceListeners(DBusServiceListenerList& dbusServiceListenerList,
568 const AvailabilityStatus& availabilityStatus) {
569 for (auto& dbusServiceListener : dbusServiceListenerList) {
570 dbusServiceListener(availabilityStatus);
574 bool DBusServiceRegistry::isDBusServiceName(const std::string& name) {
575 return name[0] != ':';
579 }// namespace CommonAPI