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 = sdbusplus::message::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(sdbusplus::message::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 =
187         sdbusplus::message::variant_ns::get<AssociationList>(attr->second);
188     if (assocs.empty())
189     {
190         // No associations skip
191         return;
192     }
193 
194     for (const auto& item : assocs)
195     {
196         if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
197         {
198             removeWatches.emplace_back(
199                 std::make_unique<Remove>(bus, std::get<2>(item)));
200             action(bus, std::get<2>(item), true);
201         }
202     }
203 
204     return;
205 }
206 
207 void getLoggingSubTree(sdbusplus::bus::bus& bus, MapperResponseType& subtree)
208 {
209     auto depth = 0;
210     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
211                                           MAPPER_IFACE, "GetSubTree");
212     mapperCall.append("/");
213     mapperCall.append(depth);
214     mapperCall.append(std::vector<Interface>({LOG_IFACE}));
215 
216     try
217     {
218         auto mapperResponseMsg = bus.call(mapperCall);
219         if (mapperResponseMsg.is_method_error())
220         {
221             using namespace xyz::openbmc_project::Led::Mapper;
222             report<MethodErr>(MethodError::METHOD_NAME("GetSubTree"),
223                               MethodError::PATH(MAPPER_OBJ_PATH),
224                               MethodError::INTERFACE(OBJMGR_IFACE));
225             return;
226         }
227 
228         try
229         {
230             mapperResponseMsg.read(subtree);
231         }
232         catch (const sdbusplus::exception::SdBusError& e)
233         {
234             log<level::ERR>(
235                 "Failed to parse existing callouts subtree message",
236                 entry("ERROR=%s", e.what()),
237                 entry("REPLY_SIG=%s", mapperResponseMsg.get_signature()));
238         }
239     }
240     catch (const sdbusplus::exception::SdBusError& e)
241     {
242         // Just means no log entries at the moment
243     }
244 }
245 
246 void Add::processExistingCallouts(sdbusplus::bus::bus& bus)
247 {
248     MapperResponseType mapperResponse;
249 
250     getLoggingSubTree(bus, mapperResponse);
251     if (mapperResponse.empty())
252     {
253         // No errors to process.
254         return;
255     }
256 
257     for (const auto& elem : mapperResponse)
258     {
259         auto method = bus.new_method_call(
260             elem.second.begin()->first.c_str(), elem.first.c_str(),
261             "org.freedesktop.DBus.Properties", "Get");
262         method.append("xyz.openbmc_project.Association.Definitions");
263         method.append("Associations");
264         auto reply = bus.call(method);
265         if (reply.is_method_error())
266         {
267             // do not stop, continue with next elog
268             log<level::ERR>("Error in getting associations");
269             continue;
270         }
271 
272         sdbusplus::message::variant<AssociationList> assoc;
273         try
274         {
275             reply.read(assoc);
276         }
277         catch (const sdbusplus::exception::SdBusError& e)
278         {
279             log<level::ERR>(
280                 "Failed to parse existing callouts associations message",
281                 entry("ERROR=%s", e.what()),
282                 entry("REPLY_SIG=%s", reply.get_signature()));
283             continue;
284         }
285         auto& assocs =
286             sdbusplus::message::variant_ns::get<AssociationList>(assoc);
287         if (assocs.empty())
288         {
289             // no associations, skip
290             continue;
291         }
292 
293         for (const auto& item : assocs)
294         {
295             if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
296             {
297                 removeWatches.emplace_back(
298                     std::make_unique<Remove>(bus, std::get<2>(item)));
299                 action(bus, std::get<2>(item), true);
300             }
301         }
302     }
303 }
304 
305 void Remove::removed(sdbusplus::message::message& msg)
306 {
307     auto bus = msg.get_bus();
308 
309     action(bus, inventoryPath, false);
310     return;
311 }
312 
313 } // namespace monitor
314 } // namespace fault
315 } // namespace fru
316 } // namespace led
317 } // namespace phosphor
318