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