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