#include "fru-fault-monitor.hpp" #include #include #include #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::unordered_map; using InterfaceName = std::string; using InterfaceMap = std::unordered_map; using Service = std::string; using Path = std::string; using Interface = std::string; using Interfaces = std::vector; using MapperResponseType = std::unordered_map>; using ResourceNotFoundErr = sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; using InvalidArgumentErr = sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; std::string getService(sdbusplus::bus_t& 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})); std::unordered_map> mapperResponse; try { auto mapperResponseMsg = bus.call(mapper); mapperResponseMsg.read(mapperResponse); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to parse getService mapper response, ERROR = {ERROR}", "ERROR", e); using namespace xyz::openbmc_project::Common; elog(ResourceNotFound::RESOURCE(path.c_str())); } if (mapperResponse.empty()) { using namespace xyz::openbmc_project::Common; elog(ResourceNotFound::RESOURCE(path.c_str())); return {}; } return mapperResponse.cbegin()->first; } void action(sdbusplus::bus_t& bus, const std::string& path, bool assert) { std::string service; try { std::string groups{LED_GROUPS}; groups.pop_back(); service = getService(bus, groups); } catch (const ResourceNotFoundErr& e) { commit(); return; } auto pos = path.rfind("/"); if (pos == std::string::npos) { using namespace xyz::openbmc_project::Common; report( InvalidArgument::ARGUMENT_NAME("path"), InvalidArgument::ARGUMENT_VALUE(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_t& e) { // Log an info message, system may not have all the LED Groups defined lg2::info("Failed to Assert LED Group, ERROR = {ERROR}", "ERROR", e); } return; } void Add::created(sdbusplus::message_t& msg) { auto bus = msg.get_bus(); sdbusplus::message::object_path objectPath; InterfaceMap interfaces; try { msg.read(objectPath, interfaces); } catch (const sdbusplus::exception_t& e) { lg2::error("Failed to parse created message, ERROR = {ERROR}", "ERROR", e); 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. // TODO:(phosphor-logging#25): support sdbusplus::message::object_path // directly. lg2::info("{PATH} created", "PATH", objectPath.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_t& 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); mapperResponseMsg.read(subtree); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to parse existing callouts subtree message, ERROR = {ERROR}", "ERROR", e); } } void Add::processExistingCallouts(sdbusplus::bus_t& 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 lg2::error("Error in getting associations"); continue; } std::variant assoc; try { reply.read(assoc); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to parse existing callouts associations message, ERROR = {ERROR}", "ERROR", e); 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_t& msg) { auto bus = msg.get_bus(); action(bus, inventoryPath, false); return; } } // namespace monitor } // namespace fault } // namespace fru } // namespace led } // namespace phosphor