1 #include "fru-fault-monitor.hpp" 2 3 #include <phosphor-logging/elog-errors.hpp> 4 #include <phosphor-logging/elog.hpp> 5 #include <phosphor-logging/lg2.hpp> 6 #include <sdbusplus/exception.hpp> 7 #include <xyz/openbmc_project/Common/error.hpp> 8 9 namespace phosphor 10 { 11 namespace led 12 { 13 namespace fru 14 { 15 namespace fault 16 { 17 namespace monitor 18 { 19 20 using namespace phosphor::logging; 21 22 static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper"; 23 static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper"; 24 static constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 25 static constexpr auto objMgrIntf = "org.freedesktop.DBus.ObjectManager"; 26 static constexpr auto ledGroups = "/xyz/openbmc_project/led/groups/"; 27 static constexpr auto logIntf = "xyz.openbmc_project.Logging.Entry"; 28 29 using AssociationList = 30 std::vector<std::tuple<std::string, std::string, std::string>>; 31 using Attributes = std::variant<bool, AssociationList>; 32 using PropertyName = std::string; 33 using PropertyMap = std::unordered_map<PropertyName, Attributes>; 34 using InterfaceName = std::string; 35 using InterfaceMap = std::unordered_map<InterfaceName, PropertyMap>; 36 37 using Service = std::string; 38 using Path = std::string; 39 using Interface = std::string; 40 using Interfaces = std::vector<Interface>; 41 using MapperResponseType = 42 std::unordered_map<Path, std::unordered_map<Service, Interfaces>>; 43 44 using ResourceNotFoundErr = 45 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 46 using InvalidArgumentErr = 47 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 48 49 std::string getService(sdbusplus::bus_t& bus, const std::string& path) 50 { 51 auto mapper = bus.new_method_call(mapperBusName, mapperObjPath, mapperIntf, 52 "GetObject"); 53 mapper.append(path.c_str(), std::vector<std::string>({objMgrIntf})); 54 55 std::unordered_map<std::string, std::vector<std::string>> mapperResponse; 56 try 57 { 58 auto mapperResponseMsg = bus.call(mapper); 59 mapperResponseMsg.read(mapperResponse); 60 } 61 catch (const sdbusplus::exception_t& e) 62 { 63 lg2::error( 64 "Failed to parse getService mapper response, ERROR = {ERROR}", 65 "ERROR", e); 66 using namespace xyz::openbmc_project::Common; 67 elog<ResourceNotFoundErr>(ResourceNotFound::RESOURCE(path.c_str())); 68 } 69 if (mapperResponse.empty()) 70 { 71 using namespace xyz::openbmc_project::Common; 72 elog<ResourceNotFoundErr>(ResourceNotFound::RESOURCE(path.c_str())); 73 return {}; 74 } 75 76 return mapperResponse.cbegin()->first; 77 } 78 79 void action(sdbusplus::bus_t& bus, const std::string& path, bool assert) 80 { 81 std::string service; 82 try 83 { 84 std::string groups{ledGroups}; 85 groups.pop_back(); 86 service = getService(bus, groups); 87 } 88 catch (const ResourceNotFoundErr& e) 89 { 90 commit<ResourceNotFoundErr>(); 91 return; 92 } 93 94 auto pos = path.rfind("/"); 95 if (pos == std::string::npos) 96 { 97 using namespace xyz::openbmc_project::Common; 98 report<InvalidArgumentErr>( 99 InvalidArgument::ARGUMENT_NAME("path"), 100 InvalidArgument::ARGUMENT_VALUE(path.c_str())); 101 return; 102 } 103 auto unit = path.substr(pos + 1); 104 105 std::string ledPath = ledGroups + unit + '_' + LED_FAULT; 106 107 auto method = bus.new_method_call(service.c_str(), ledPath.c_str(), 108 "org.freedesktop.DBus.Properties", "Set"); 109 method.append("xyz.openbmc_project.Led.Group"); 110 method.append("Asserted"); 111 112 method.append(std::variant<bool>(assert)); 113 114 try 115 { 116 bus.call_noreply(method); 117 } 118 catch (const sdbusplus::exception_t& e) 119 { 120 // Log an info message, system may not have all the LED Groups defined 121 lg2::info("Failed to Assert LED Group, ERROR = {ERROR}", "ERROR", e); 122 } 123 124 return; 125 } 126 127 void Add::created(sdbusplus::message_t& msg) 128 { 129 auto bus = msg.get_bus(); 130 131 sdbusplus::message::object_path objectPath; 132 InterfaceMap interfaces; 133 try 134 { 135 msg.read(objectPath, interfaces); 136 } 137 catch (const sdbusplus::exception_t& e) 138 { 139 lg2::error("Failed to parse created message, ERROR = {ERROR}", "ERROR", 140 e); 141 return; 142 } 143 144 std::size_t found = objectPath.str.find(ELOG_ENTRY); 145 if (found == std::string::npos) 146 { 147 // Not a new error entry skip 148 return; 149 } 150 auto iter = interfaces.find("xyz.openbmc_project.Association.Definitions"); 151 if (iter == interfaces.end()) 152 { 153 return; 154 } 155 156 // Nothing else shows when a specific error log 157 // has been created. Do it here. 158 lg2::info("{PATH} created", "PATH", objectPath); 159 160 auto attr = iter->second.find("Associations"); 161 if (attr == iter->second.end()) 162 { 163 return; 164 } 165 166 auto& assocs = std::get<AssociationList>(attr->second); 167 168 for (const auto& item : assocs) 169 { 170 if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) 171 { 172 removeWatches.emplace_back( 173 std::make_unique<Remove>(bus, std::get<2>(item))); 174 action(bus, std::get<2>(item), true); 175 } 176 } 177 178 return; 179 } 180 181 void getLoggingSubTree(sdbusplus::bus_t& bus, MapperResponseType& subtree) 182 { 183 auto depth = 0; 184 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 185 mapperIntf, "GetSubTree"); 186 mapperCall.append("/"); 187 mapperCall.append(depth); 188 mapperCall.append(std::vector<Interface>({logIntf})); 189 190 try 191 { 192 auto mapperResponseMsg = bus.call(mapperCall); 193 mapperResponseMsg.read(subtree); 194 } 195 catch (const sdbusplus::exception_t& e) 196 { 197 lg2::error( 198 "Failed to parse existing callouts subtree message, ERROR = {ERROR}", 199 "ERROR", e); 200 } 201 } 202 203 void Add::processExistingCallouts(sdbusplus::bus_t& bus) 204 { 205 MapperResponseType mapperResponse; 206 207 getLoggingSubTree(bus, mapperResponse); 208 if (mapperResponse.empty()) 209 { 210 // No errors to process. 211 return; 212 } 213 214 for (const auto& elem : mapperResponse) 215 { 216 auto method = bus.new_method_call( 217 elem.second.begin()->first.c_str(), elem.first.c_str(), 218 "org.freedesktop.DBus.Properties", "Get"); 219 method.append("xyz.openbmc_project.Association.Definitions"); 220 method.append("Associations"); 221 auto reply = bus.call(method); 222 if (reply.is_method_error()) 223 { 224 // do not stop, continue with next elog 225 lg2::error("Error in getting associations"); 226 continue; 227 } 228 229 std::variant<AssociationList> assoc; 230 try 231 { 232 reply.read(assoc); 233 } 234 catch (const sdbusplus::exception_t& e) 235 { 236 lg2::error( 237 "Failed to parse existing callouts associations message, ERROR = {ERROR}", 238 "ERROR", e); 239 continue; 240 } 241 auto& assocs = std::get<AssociationList>(assoc); 242 243 for (const auto& item : assocs) 244 { 245 if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) 246 { 247 removeWatches.emplace_back( 248 std::make_unique<Remove>(bus, std::get<2>(item))); 249 action(bus, std::get<2>(item), true); 250 } 251 } 252 } 253 } 254 255 void Remove::removed(sdbusplus::message_t& msg) 256 { 257 auto bus = msg.get_bus(); 258 259 action(bus, inventoryPath, false); 260 return; 261 } 262 263 } // namespace monitor 264 } // namespace fault 265 } // namespace fru 266 } // namespace led 267 } // namespace phosphor 268