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