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 if (assocs.empty()) 168 { 169 // No associations skip 170 return; 171 } 172 173 for (const auto& item : assocs) 174 { 175 if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) 176 { 177 removeWatches.emplace_back( 178 std::make_unique<Remove>(bus, std::get<2>(item))); 179 action(bus, std::get<2>(item), true); 180 } 181 } 182 183 return; 184 } 185 186 void getLoggingSubTree(sdbusplus::bus_t& bus, MapperResponseType& subtree) 187 { 188 auto depth = 0; 189 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 190 mapperIntf, "GetSubTree"); 191 mapperCall.append("/"); 192 mapperCall.append(depth); 193 mapperCall.append(std::vector<Interface>({logIntf})); 194 195 try 196 { 197 auto mapperResponseMsg = bus.call(mapperCall); 198 mapperResponseMsg.read(subtree); 199 } 200 catch (const sdbusplus::exception_t& e) 201 { 202 lg2::error( 203 "Failed to parse existing callouts subtree message, ERROR = {ERROR}", 204 "ERROR", e); 205 } 206 } 207 208 void Add::processExistingCallouts(sdbusplus::bus_t& bus) 209 { 210 MapperResponseType mapperResponse; 211 212 getLoggingSubTree(bus, mapperResponse); 213 if (mapperResponse.empty()) 214 { 215 // No errors to process. 216 return; 217 } 218 219 for (const auto& elem : mapperResponse) 220 { 221 auto method = bus.new_method_call( 222 elem.second.begin()->first.c_str(), elem.first.c_str(), 223 "org.freedesktop.DBus.Properties", "Get"); 224 method.append("xyz.openbmc_project.Association.Definitions"); 225 method.append("Associations"); 226 auto reply = bus.call(method); 227 if (reply.is_method_error()) 228 { 229 // do not stop, continue with next elog 230 lg2::error("Error in getting associations"); 231 continue; 232 } 233 234 std::variant<AssociationList> assoc; 235 try 236 { 237 reply.read(assoc); 238 } 239 catch (const sdbusplus::exception_t& e) 240 { 241 lg2::error( 242 "Failed to parse existing callouts associations message, ERROR = {ERROR}", 243 "ERROR", e); 244 continue; 245 } 246 auto& assocs = std::get<AssociationList>(assoc); 247 if (assocs.empty()) 248 { 249 // no associations, skip 250 continue; 251 } 252 253 for (const auto& item : assocs) 254 { 255 if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0) 256 { 257 removeWatches.emplace_back( 258 std::make_unique<Remove>(bus, std::get<2>(item))); 259 action(bus, std::get<2>(item), true); 260 } 261 } 262 } 263 } 264 265 void Remove::removed(sdbusplus::message_t& msg) 266 { 267 auto bus = msg.get_bus(); 268 269 action(bus, inventoryPath, false); 270 return; 271 } 272 273 } // namespace monitor 274 } // namespace fault 275 } // namespace fru 276 } // namespace led 277 } // namespace phosphor 278