/** * Copyright © 2016 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "manager.hpp" #include "errors.hpp" #include #include #include #include #include #include using namespace std::literals::chrono_literals; namespace phosphor { namespace inventory { namespace manager { /** @brief Fowrarding signal callback. * * Extracts per-signal specific context and forwards the call to the manager * instance. */ auto _signal(sd_bus_message* m, void* data, sd_bus_error* e) noexcept { try { auto msg = sdbusplus::message::message(m); auto& args = *static_cast(data); sd_bus_message_ref(m); auto& mgr = *std::get<0>(args); mgr.handleEvent(msg, static_cast(*std::get<1>(args)), *std::get<2>(args)); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } return 0; } Manager::Manager(sdbusplus::bus::bus&& bus, const char* busname, const char* root, const char* iface) : ServerObject(bus, root), _shutdown(false), _root(root), _bus(std::move(bus)), _manager(_bus, root) { for (auto& group : _events) { for (auto pEvent : std::get>(group)) { if (pEvent->type != Event::Type::DBUS_SIGNAL) { continue; } // Create a callback context for this event group. auto dbusEvent = static_cast(pEvent.get()); // Go ahead and store an iterator pointing at // the event data to avoid lookups later since // additional signal callbacks aren't added // after the manager is constructed. _sigargs.emplace_back( std::make_unique(this, dbusEvent, &group)); // Register our callback and the context for // each signal event. _matches.emplace_back(_bus, dbusEvent->signature, _signal, _sigargs.back().get()); } } _bus.request_name(busname); // Restore any persistent inventory restore(); } void Manager::shutdown() noexcept { _shutdown = true; } void Manager::run() noexcept { sdbusplus::message::message unusedMsg{nullptr}; // Run startup events. for (auto& group : _events) { for (auto pEvent : std::get>(group)) { if (pEvent->type == Event::Type::STARTUP) { handleEvent(unusedMsg, *pEvent, group); } } } while (!_shutdown) { try { _bus.process_discard(); _bus.wait((5000000us).count()); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } } } void Manager::updateInterfaces(const sdbusplus::message::object_path& path, const Object& interfaces, ObjectReferences::iterator pos, bool newObject, bool restoreFromCache) { auto& refaces = pos->second; auto ifaceit = interfaces.cbegin(); auto opsit = _makers.cbegin(); auto refaceit = refaces.begin(); std::vector signals; while (ifaceit != interfaces.cend()) { try { // Find the binding ops for this interface. opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first, compareFirst(_makers.key_comp())); if (opsit == _makers.cend() || opsit->first != ifaceit->first) { // This interface is not supported. throw InterfaceError("Encountered unsupported interface.", ifaceit->first); } // Find the binding insertion point or the binding to update. refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first, compareFirst(refaces.key_comp())); if (refaceit == refaces.end() || refaceit->first != ifaceit->first) { // Add the new interface. auto& ctor = std::get(opsit->second); refaceit = refaces.insert( refaceit, std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(), ifaceit->second))); signals.push_back(ifaceit->first); } else { // Set the new property values. auto& assign = std::get(opsit->second); assign(ifaceit->second, refaceit->second); } if (!restoreFromCache) { auto& serialize = std::get(opsit->second); serialize(path, ifaceit->first, refaceit->second); } else { auto& deserialize = std::get(opsit->second); deserialize(path, ifaceit->first, refaceit->second); } } catch (const InterfaceError& e) { // Reset the binding ops iterator since we are // at the end. opsit = _makers.cbegin(); e.log(); } ++ifaceit; } if (newObject) { _bus.emit_object_added(path.str.c_str()); } else if (!signals.empty()) { _bus.emit_interfaces_added(path.str.c_str(), signals); } } void Manager::updateObjects( const std::map& objs, bool restoreFromCache) { auto objit = objs.cbegin(); auto refit = _refs.begin(); std::string absPath; bool newObj; while (objit != objs.cend()) { // Find the insertion point or the object to update. refit = std::lower_bound(refit, _refs.end(), objit->first, compareFirst(RelPathCompare(_root))); absPath.assign(_root); absPath.append(objit->first); newObj = false; if (refit == _refs.end() || refit->first != absPath) { refit = _refs.insert( refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); newObj = true; } updateInterfaces(absPath, objit->second, refit, newObj, restoreFromCache); ++objit; } } void Manager::notify(std::map objs) { updateObjects(objs); } void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event, const EventInfo& info) { auto& actions = std::get<1>(info); for (auto& f : event) { if (!f(_bus, msg, *this)) { return; } } for (auto& action : actions) { action(_bus, *this); } } void Manager::destroyObjects(const std::vector& paths) { std::string p; for (const auto& path : paths) { p.assign(_root); p.append(path); _bus.emit_object_removed(p.c_str()); _refs.erase(p); } } void Manager::createObjects( const std::map& objs) { updateObjects(objs); } std::any& Manager::getInterfaceHolder(const char* path, const char* interface) { return const_cast( const_cast(this)->getInterfaceHolder(path, interface)); } const std::any& Manager::getInterfaceHolder(const char* path, const char* interface) const { std::string p{path}; auto oit = _refs.find(_root + p); if (oit == _refs.end()) throw std::runtime_error(_root + p + " was not found"); auto& obj = oit->second; auto iit = obj.find(interface); if (iit == obj.end()) throw std::runtime_error("interface was not found"); return iit->second; } void Manager::restore() { namespace fs = std::experimental::filesystem; if (!fs::exists(fs::path(PIM_PERSIST_PATH))) { return; } static const std::string remove = std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT; std::map objects; for (const auto& dirent : fs::recursive_directory_iterator(PIM_PERSIST_PATH)) { const auto& path = dirent.path(); if (fs::is_regular_file(path)) { auto ifaceName = path.filename().string(); auto objPath = path.parent_path().string(); objPath.erase(0, remove.length()); auto objit = objects.find(objPath); Interface propertyMap{}; if (objects.end() != objit) { auto& object = objit->second; object.emplace(std::move(ifaceName), std::move(propertyMap)); } else { Object object; object.emplace(std::move(ifaceName), std::move(propertyMap)); objects.emplace(std::move(objPath), std::move(object)); } } } if (!objects.empty()) { auto restoreFromCache = true; updateObjects(objects, restoreFromCache); } } } // namespace manager } // namespace inventory } // namespace phosphor // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4