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