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