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