/** * Copyright © 2018 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 "config.h" #include "manager.hpp" #include "callout.hpp" #include "policy_find.hpp" #include namespace ibm { namespace logging { namespace fs = std::experimental::filesystem; using namespace phosphor::logging; Manager::Manager(sdbusplus::bus_t& bus) : bus(bus), addMatch(bus, sdbusplus::bus::match::rules::interfacesAdded() + sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), std::bind(std::mem_fn(&Manager::interfaceAdded), this, std::placeholders::_1)), removeMatch(bus, sdbusplus::bus::match::rules::interfacesRemoved() + sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), std::bind(std::mem_fn(&Manager::interfaceRemoved), this, std::placeholders::_1)) #ifdef USE_POLICY_INTERFACE , policies(POLICY_JSON_PATH) #endif { createAll(); } void Manager::createAll() { try { auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH); for (const auto& object : objects) { const auto& interfaces = object.second; auto propertyMap = interfaces.find(LOGGING_IFACE); if (propertyMap != interfaces.end()) { createWithRestore(object.first, interfaces); } } } catch (const sdbusplus::exception_t& e) { log("sdbusplus error getting logging managed objects", entry("ERROR=%s", e.what())); } } void Manager::createWithRestore(const std::string& objectPath, const DbusInterfaceMap& interfaces) { createObject(objectPath, interfaces); restoreCalloutObjects(objectPath, interfaces); } void Manager::create(const std::string& objectPath, const DbusInterfaceMap& interfaces) { createObject(objectPath, interfaces); createCalloutObjects(objectPath, interfaces); } void Manager::createObject(const std::string& objectPath, const DbusInterfaceMap& interfaces) { #ifdef USE_POLICY_INTERFACE auto logInterface = interfaces.find(LOGGING_IFACE); createPolicyInterface(objectPath, logInterface->second); #endif } void Manager::erase(EntryID id) { fs::remove_all(getSaveDir(id)); childEntries.erase(id); entries.erase(id); } void Manager::addInterface(const std::string& objectPath, InterfaceType type, std::any& object) { auto id = getEntryID(objectPath); auto entry = entries.find(id); if (entry == entries.end()) { InterfaceMap interfaces; interfaces.emplace(type, object); entries.emplace(id, std::move(interfaces)); } else { entry->second.emplace(type, object); } } void Manager::addChildInterface(const std::string& objectPath, InterfaceType type, std::any& object) { auto id = getEntryID(objectPath); auto entry = childEntries.find(id); // childEntries is: // A map of error log entry IDs to: // a map of interface types to: // a vector of interface objects if (entry == childEntries.end()) { ObjectList objects{object}; InterfaceMapMulti interfaces; interfaces.emplace(type, std::move(objects)); childEntries.emplace(id, std::move(interfaces)); } else { auto i = entry->second.find(type); if (i == entry->second.end()) { ObjectList objects{object}; entry->second.emplace(type, objects); } else { i->second.emplace_back(object); } } } #ifdef USE_POLICY_INTERFACE void Manager::createPolicyInterface(const std::string& objectPath, const DbusPropertyMap& properties) { auto values = policy::find(policies, properties); auto object = std::make_shared( bus, objectPath.c_str(), PolicyObject::action::defer_emit); object->eventID(std::get(values)); object->description(std::get(values)); object->emit_object_added(); std::any anyObject = object; addInterface(objectPath, InterfaceType::POLICY, anyObject); } #endif void Manager::createCalloutObjects(const std::string& objectPath, const DbusInterfaceMap& interfaces) { // Use the associations property in the org.openbmc.Associations // interface to find any callouts. Then grab all properties on // the Asset interface for that object in the inventory to use // in our callout objects. auto associations = interfaces.find(ASSOC_IFACE); if (associations == interfaces.end()) { return; } const auto& properties = associations->second; auto assocProperty = properties.find("Associations"); auto assocValue = std::get(assocProperty->second); auto id = getEntryID(objectPath); auto calloutNum = 0; DbusSubtree subtree; for (const auto& association : assocValue) { try { if (std::get(association) != "callout") { continue; } auto callout = std::get(association); if (subtree.empty()) { subtree = getSubtree(bus, "/", 0, ASSET_IFACE); if (subtree.empty()) { break; } } auto service = getService(callout, ASSET_IFACE, subtree); if (service.empty()) { continue; } auto properties = getAllProperties(bus, service, callout, ASSET_IFACE); if (properties.empty()) { continue; } auto calloutPath = getCalloutObjectPath(objectPath, calloutNum); auto object = std::make_shared( bus, calloutPath, callout, calloutNum, getLogTimestamp(interfaces), properties); auto dir = getCalloutSaveDir(id); if (!fs::exists(dir)) { fs::create_directories(dir); } object->serialize(dir); std::any anyObject = object; addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); calloutNum++; } catch (const sdbusplus::exception_t& e) { log("sdbusplus exception", entry("ERROR=%s", e.what())); } } } void Manager::restoreCalloutObjects(const std::string& objectPath, const DbusInterfaceMap& interfaces) { auto saveDir = getCalloutSaveDir(getEntryID(objectPath)); if (!fs::exists(saveDir)) { return; } size_t id; for (auto& f : fs::directory_iterator(saveDir)) { try { id = std::stoul(f.path().filename()); } catch (const std::exception& e) { log("Invalid IBM logging callout save file. Deleting", entry("FILE=%s", f.path().c_str())); fs::remove(f.path()); continue; } auto path = getCalloutObjectPath(objectPath, id); auto callout = std::make_shared(bus, path, id, getLogTimestamp(interfaces)); if (callout->deserialize(saveDir)) { callout->emit_object_added(); std::any anyObject = callout; addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); } } } void Manager::interfaceAdded(sdbusplus::message_t& msg) { sdbusplus::message::object_path path; DbusInterfaceMap interfaces; msg.read(path, interfaces); // Find the Logging.Entry interface with all of its properties // to pass to create(). if (interfaces.find(LOGGING_IFACE) != interfaces.end()) { create(path, interfaces); } } uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces) { auto interface = interfaces.find(LOGGING_IFACE); if (interface != interfaces.end()) { auto property = interface->second.find("Timestamp"); if (property != interface->second.end()) { return std::get(property->second); } } return 0; } fs::path Manager::getSaveDir(EntryID id) { return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id); } fs::path Manager::getCalloutSaveDir(EntryID id) { return getSaveDir(id) / "callouts"; } std::string Manager::getCalloutObjectPath(const std::string& objectPath, uint32_t calloutNum) { return fs::path{objectPath} / "callouts" / std::to_string(calloutNum); } void Manager::interfaceRemoved(sdbusplus::message_t& msg) { sdbusplus::message::object_path path; DbusInterfaceList interfaces; msg.read(path, interfaces); // If the Logging.Entry interface was removed, then remove // our object auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE); if (i != interfaces.end()) { erase(getEntryID(path)); } } } // namespace logging } // namespace ibm