#include "fru-fault-monitor.hpp" #include "elog-errors.hpp" #include "xyz/openbmc_project/Led/Fru/Monitor/error.hpp" #include "xyz/openbmc_project/Led/Mapper/error.hpp" #include #include namespace phosphor { namespace led { namespace fru { namespace fault { namespace monitor { using namespace phosphor::logging; constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; constexpr auto MAPPER_OBJ_PATH = "/xyz/openbmc_project/object_mapper"; constexpr auto MAPPER_IFACE = "xyz.openbmc_project.ObjectMapper"; constexpr auto OBJMGR_IFACE = "org.freedesktop.DBus.ObjectManager"; constexpr auto LED_GROUPS = "/xyz/openbmc_project/led/groups/"; constexpr auto LOG_PATH = "/xyz/openbmc_project/logging"; constexpr auto LOG_IFACE = "xyz.openbmc_project.Logging.Entry"; using AssociationList = std::vector>; using Attributes = std::variant; using PropertyName = std::string; using PropertyMap = std::map; using InterfaceName = std::string; using InterfaceMap = std::map; using Service = std::string; using Path = std::string; using Interface = std::string; using Interfaces = std::vector; using MapperResponseType = std::map>; using MethodErr = sdbusplus::xyz::openbmc_project::Led::Mapper::Error::MethodError; using ObjectNotFoundErr = sdbusplus::xyz::openbmc_project::Led::Mapper::Error::ObjectNotFoundError; using InventoryPathErr = sdbusplus::xyz::openbmc_project::Led::Fru::Monitor:: Error::InventoryPathError; std::string getService(sdbusplus::bus::bus& bus, const std::string& path) { auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH, MAPPER_IFACE, "GetObject"); mapper.append(path.c_str(), std::vector({OBJMGR_IFACE})); auto mapperResponseMsg = bus.call(mapper); if (mapperResponseMsg.is_method_error()) { using namespace xyz::openbmc_project::Led::Mapper; elog(MethodError::METHOD_NAME("GetObject"), MethodError::PATH(path.c_str()), MethodError::INTERFACE(OBJMGR_IFACE)); } std::map> mapperResponse; try { mapperResponseMsg.read(mapperResponse); } catch (const sdbusplus::exception::SdBusError& e) { log( "Failed to parse getService mapper response", entry("ERROR=%s", e.what()), entry("REPLY_SIG=%s", mapperResponseMsg.get_signature())); using namespace xyz::openbmc_project::Led::Mapper; elog(ObjectNotFoundError::METHOD_NAME("GetObject"), ObjectNotFoundError::PATH(path.c_str()), ObjectNotFoundError::INTERFACE(OBJMGR_IFACE)); } if (mapperResponse.empty()) { using namespace xyz::openbmc_project::Led::Mapper; elog(ObjectNotFoundError::METHOD_NAME("GetObject"), ObjectNotFoundError::PATH(path.c_str()), ObjectNotFoundError::INTERFACE(OBJMGR_IFACE)); } return mapperResponse.cbegin()->first; } void action(sdbusplus::bus::bus& bus, const std::string& path, bool assert) { std::string service; try { std::string groups{LED_GROUPS}; groups.pop_back(); service = getService(bus, groups); } catch (MethodErr& e) { commit(); return; } catch (ObjectNotFoundErr& e) { commit(); return; } auto pos = path.rfind("/"); if (pos == std::string::npos) { using namespace xyz::openbmc_project::Led::Fru::Monitor; report(InventoryPathError::PATH(path.c_str())); return; } auto unit = path.substr(pos + 1); std::string ledPath = LED_GROUPS + unit + '_' + LED_FAULT; auto method = bus.new_method_call(service.c_str(), ledPath.c_str(), "org.freedesktop.DBus.Properties", "Set"); method.append("xyz.openbmc_project.Led.Group"); method.append("Asserted"); method.append(std::variant(assert)); try { bus.call_noreply(method); } catch (const sdbusplus::exception::SdBusError& e) { // Log an info message, system may not have all the LED Groups defined log("Failed to Assert LED Group", entry("ERROR=%s", e.what())); } return; } void Add::created(sdbusplus::message::message& msg) { auto bus = msg.get_bus(); sdbusplus::message::object_path objectPath; InterfaceMap interfaces; try { msg.read(objectPath, interfaces); } catch (const sdbusplus::exception::SdBusError& e) { log("Failed to parse created message", entry("ERROR=%s", e.what()), entry("REPLY_SIG=%s", msg.get_signature())); return; } std::size_t found = objectPath.str.find(ELOG_ENTRY); if (found == std::string::npos) { // Not a new error entry skip return; } auto iter = interfaces.find("xyz.openbmc_project.Association.Definitions"); if (iter == interfaces.end()) { return; } // Nothing else shows when a specific error log // has been created. Do it here. std::string message{objectPath.str + " created"}; log(message.c_str()); auto attr = iter->second.find("Associations"); if (attr == iter->second.end()) { return; } auto& assocs = std::get(attr->second); if (assocs.empty()) { // No associations skip return; } for (const auto& item : assocs) { if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) { removeWatches.emplace_back( std::make_unique(bus, std::get<2>(item))); action(bus, std::get<2>(item), true); } } return; } void getLoggingSubTree(sdbusplus::bus::bus& bus, MapperResponseType& subtree) { auto depth = 0; auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH, MAPPER_IFACE, "GetSubTree"); mapperCall.append("/"); mapperCall.append(depth); mapperCall.append(std::vector({LOG_IFACE})); try { auto mapperResponseMsg = bus.call(mapperCall); if (mapperResponseMsg.is_method_error()) { using namespace xyz::openbmc_project::Led::Mapper; report(MethodError::METHOD_NAME("GetSubTree"), MethodError::PATH(MAPPER_OBJ_PATH), MethodError::INTERFACE(OBJMGR_IFACE)); return; } try { mapperResponseMsg.read(subtree); } catch (const sdbusplus::exception::SdBusError& e) { log( "Failed to parse existing callouts subtree message", entry("ERROR=%s", e.what()), entry("REPLY_SIG=%s", mapperResponseMsg.get_signature())); } } catch (const sdbusplus::exception::SdBusError& e) { // Just means no log entries at the moment } } void Add::processExistingCallouts(sdbusplus::bus::bus& bus) { MapperResponseType mapperResponse; getLoggingSubTree(bus, mapperResponse); if (mapperResponse.empty()) { // No errors to process. return; } for (const auto& elem : mapperResponse) { auto method = bus.new_method_call( elem.second.begin()->first.c_str(), elem.first.c_str(), "org.freedesktop.DBus.Properties", "Get"); method.append("xyz.openbmc_project.Association.Definitions"); method.append("Associations"); auto reply = bus.call(method); if (reply.is_method_error()) { // do not stop, continue with next elog log("Error in getting associations"); continue; } std::variant assoc; try { reply.read(assoc); } catch (const sdbusplus::exception::SdBusError& e) { log( "Failed to parse existing callouts associations message", entry("ERROR=%s", e.what()), entry("REPLY_SIG=%s", reply.get_signature())); continue; } auto& assocs = std::get(assoc); if (assocs.empty()) { // no associations, skip continue; } for (const auto& item : assocs) { if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) { removeWatches.emplace_back( std::make_unique(bus, std::get<2>(item))); action(bus, std::get<2>(item), true); } } } } void Remove::removed(sdbusplus::message::message& msg) { auto bus = msg.get_bus(); action(bus, inventoryPath, false); return; } } // namespace monitor } // namespace fault } // namespace fru } // namespace led } // namespace phosphor