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