// Copyright (C) 2013-2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #if !defined (COMMONAPI_INTERNAL_COMPILATION) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_ #define COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CommonAPI { namespace DBus { typedef Event NameOwnerChangedEvent; typedef Event::Subscription NameOwnerChangedEventSubscription; // Connection name, Object path typedef std::pair DBusInstanceId; class DBusAddress; class DBusAddressTranslator; class DBusDaemonProxy; class COMMONAPI_EXPORT DBusServiceRegistry: public std::enable_shared_from_this, public DBusProxyConnection::DBusSignalHandler { public: enum class DBusRecordState { UNKNOWN, AVAILABLE, RESOLVING, RESOLVED, NOT_AVAILABLE }; // template class DBusServiceListener<> { typedef functor; typedef list; typedef subscription } typedef std::function, const AvailabilityStatus& availabilityStatus)> DBusServiceListener; typedef long int DBusServiceSubscription; struct DBusServiceListenerInfo { DBusServiceListener listener; std::weak_ptr proxy; }; typedef std::map> DBusServiceListenerList; typedef std::function& interfaces, const AvailabilityStatus& availabilityStatus)> DBusManagedInterfaceListener; typedef std::list DBusManagedInterfaceListenerList; typedef DBusManagedInterfaceListenerList::iterator DBusManagedInterfaceSubscription; typedef std::function GetAvailableServiceInstancesCallback; static std::shared_ptr get(const std::shared_ptr &_connection, bool _insert=true); static void remove(const std::shared_ptr &_connection); DBusServiceRegistry(std::shared_ptr dbusProxyConnection); DBusServiceRegistry(const DBusServiceRegistry&) = delete; DBusServiceRegistry& operator=(const DBusServiceRegistry&) = delete; virtual ~DBusServiceRegistry(); void init(); DBusServiceSubscription subscribeAvailabilityListener(const std::string &_address, DBusServiceListener _listener, std::weak_ptr _proxy); void unsubscribeAvailabilityListener(const std::string &_address, DBusServiceSubscription &_listener); bool isServiceInstanceAlive(const std::string &_dbusInterfaceName, const std::string &_dbusConnectionName, const std::string &_dbusObjectPath); virtual void getAvailableServiceInstances(const std::string& dbusServiceName, const std::string& dbusObjectPath, DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& availableServiceInstances); virtual void getAvailableServiceInstancesAsync(GetAvailableServiceInstancesCallback callback, const std::string& dbusServiceName, const std::string& dbusObjectPath); virtual void onSignalDBusMessage(const DBusMessage&); void setDBusServicePredefined(const std::string& _serviceName); private: struct DBusInterfaceNameListenersRecord { DBusInterfaceNameListenersRecord() : state(DBusRecordState::UNKNOWN), nextSubscriptionKey(0) { } DBusRecordState state; DBusServiceListenerList listenerList; std::list listenersToRemove; DBusServiceSubscription nextSubscriptionKey; }; typedef std::unordered_map DBusInterfaceNameListenersMap; struct DBusServiceListenersRecord { DBusServiceListenersRecord() : uniqueBusNameState(DBusRecordState::UNKNOWN), promiseOnResolve(std::make_shared>()), mutexOnResolve() { } ~DBusServiceListenersRecord() { if(uniqueBusNameState == DBusRecordState::RESOLVING && futureOnResolve.valid()) promiseOnResolve->set_value(DBusRecordState::NOT_AVAILABLE); }; DBusRecordState uniqueBusNameState; std::string uniqueBusName; std::shared_ptr> promiseOnResolve; std::shared_future futureOnResolve; std::unique_lock* mutexOnResolve; std::unordered_map dbusObjectPathListenersMap; }; std::unordered_map dbusServiceListenersMap; struct DBusObjectPathCache { DBusObjectPathCache() : referenceCount(0), state(DBusRecordState::UNKNOWN), promiseOnResolve(std::make_shared>()), pendingObjectManagerCalls(0) { } ~DBusObjectPathCache() { if(state == DBusRecordState::RESOLVING && futureOnResolve.valid()) promiseOnResolve->set_value(DBusRecordState::NOT_AVAILABLE); } size_t referenceCount; DBusRecordState state; std::shared_ptr> promiseOnResolve; std::shared_future futureOnResolve; std::string serviceName; std::unordered_set dbusInterfaceNamesCache; uint8_t pendingObjectManagerCalls; }; struct DBusUniqueNameRecord { DBusUniqueNameRecord() : objectPathsState(DBusRecordState::UNKNOWN) { } std::string uniqueName; DBusRecordState objectPathsState; std::unordered_set ownedBusNames; std::unordered_map dbusObjectPathsCache; }; std::unordered_map dbusUniqueNamesMap_; typedef std::unordered_map::iterator DBusUniqueNamesMapIterator; // mapping service names (well-known names) to service instances std::unordered_map dbusServiceNameMap_; // protects the dbus service maps std::recursive_mutex dbusServicesMutex_; void resolveDBusServiceName(const std::string& dbusServiceName, DBusServiceListenersRecord& dbusServiceListenersRecord); void onGetNameOwnerCallback(const CallStatus& status, std::string dbusServiceUniqueName, const std::string& dbusServiceName); DBusRecordState resolveDBusInterfaceNameState(const DBusAddress &_address, DBusServiceListenersRecord &_record); DBusObjectPathCache& getDBusObjectPathCacheReference(const std::string& dbusObjectPath, const std::string& dbusServiceName, const std::string& dbusServiceUniqueName, DBusUniqueNameRecord& dbusUniqueNameRecord); bool resolveObjectPathWithObjectManager(DBusObjectPathCache& dbusObjectPathRecord, const std::string& dbusServiceUniqueName, const std::string& dbusObjectPath); typedef std::function GetManagedObjectsCallback; bool getManagedObjects(const std::string& dbusServiceName, const std::string& dbusObjectPath, DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& availableServiceInstances); bool getManagedObjectsAsync(const std::string& dbusServiceName, const std::string& dbusObjectPath, GetManagedObjectsCallback callback); void onGetManagedObjectsCallbackResolve(const CallStatus& callStatus, const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict availableServiceInstances, const std::string& dbusServiceUniqueName, const std::string& dbusObjectPath); void onDBusDaemonProxyNameOwnerChangedEvent(const std::string& name, const std::string& oldOwner, const std::string& newOwner); std::shared_ptr dbusDaemonProxy_; bool initialized_; ProxyStatusEvent::Subscription dbusDaemonProxyStatusEventSubscription_; NameOwnerChangedEvent::Subscription dbusDaemonProxyNameOwnerChangedEventSubscription_; void checkDBusServiceWasAvailable(const std::string& dbusServiceName, const std::string& dbusServiceUniqueName); void onDBusServiceAvailable(const std::string& dbusServiceName, const std::string& dbusServiceUniqueName); void onDBusServiceNotAvailable(DBusServiceListenersRecord& dbusServiceListenersRecord, const std::string &_serviceName = ""); void notifyDBusServiceListenersLocked(const DBusUniqueNameRecord _dbusUniqueNameRecord, const std::string _dbusObjectPath, const std::unordered_set _dbusInterfaceNames, const DBusRecordState _dbusInterfaceNamesState); void notifyDBusServiceListeners(const DBusUniqueNameRecord& dbusUniqueNameRecord, const std::string& dbusObjectPath, const std::unordered_set& dbusInterfaceNames, const DBusRecordState& dbusInterfaceNamesState); void notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, const std::unordered_set& dbusInterfaceNames); void notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, const std::unordered_set& dbusInterfaceNames, const DBusRecordState& dbusInterfaceNamesState); void notifyDBusInterfaceNameListeners(DBusInterfaceNameListenersRecord& dbusInterfaceNameListenersRecord, const bool& isDBusInterfaceNameAvailable); void removeUniqueName(const DBusUniqueNamesMapIterator &_dbusUniqueName, const std::string &_serviceName); DBusUniqueNameRecord* insertServiceNameMapping(const std::string& dbusUniqueName, const std::string& dbusServiceName); bool findCachedDbusService(const std::string& dbusServiceName, DBusUniqueNameRecord** uniqueNameRecord); bool findCachedObjectPath(const std::string& dbusObjectPathName, const DBusUniqueNameRecord* uniqueNameRecord, DBusObjectPathCache* objectPathCache); void fetchAllServiceNames(); inline bool isDBusServiceName(const std::string &_name) { return (_name.length() > 0 && _name[0] != ':'); }; inline bool isOrgFreedesktopDBusInterface(const std::string& dbusInterfaceName) { return dbusInterfaceName.find("org.freedesktop.DBus.") == 0; } std::thread::id notificationThread_; std::unordered_set dbusPredefinedServices_; private: typedef std::map> RegistryMap_t; static std::shared_ptr getRegistryMap() { static std::shared_ptr registries(new RegistryMap_t); return registries; } static std::mutex registriesMutex_; std::shared_ptr registries_; std::shared_ptr translator_; std::weak_ptr selfReference_; }; } // namespace DBus } // namespace CommonAPI #endif // COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_