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