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