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 static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
23 static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
24 static constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
25 static constexpr auto objMgrIntf = "org.freedesktop.DBus.ObjectManager";
26 static constexpr auto ledGroups = "/xyz/openbmc_project/led/groups/";
27 static constexpr auto logIntf = "xyz.openbmc_project.Logging.Entry";
28 
29 using AssociationList =
30     std::vector<std::tuple<std::string, std::string, std::string>>;
31 using Attributes = std::variant<bool, AssociationList>;
32 using PropertyName = std::string;
33 using PropertyMap = std::unordered_map<PropertyName, Attributes>;
34 using InterfaceName = std::string;
35 using InterfaceMap = std::unordered_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 =
42     std::unordered_map<Path, std::unordered_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_t& bus, const std::string& path)
50 {
51     auto mapper = bus.new_method_call(mapperBusName, mapperObjPath, mapperIntf,
52                                       "GetObject");
53     mapper.append(path.c_str(), std::vector<std::string>({objMgrIntf}));
54 
55     std::unordered_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_t& 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_t& bus, const std::string& path, bool assert)
80 {
81     std::string service;
82     try
83     {
84         std::string groups{ledGroups};
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 = ledGroups + 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_t& 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_t& 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_t& 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     lg2::info("{PATH} created", "PATH", objectPath);
159 
160     auto attr = iter->second.find("Associations");
161     if (attr == iter->second.end())
162     {
163         return;
164     }
165 
166     auto& assocs = std::get<AssociationList>(attr->second);
167     if (assocs.empty())
168     {
169         // No associations skip
170         return;
171     }
172 
173     for (const auto& item : assocs)
174     {
175         if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
176         {
177             removeWatches.emplace_back(
178                 std::make_unique<Remove>(bus, std::get<2>(item)));
179             action(bus, std::get<2>(item), true);
180         }
181     }
182 
183     return;
184 }
185 
186 void getLoggingSubTree(sdbusplus::bus_t& bus, MapperResponseType& subtree)
187 {
188     auto depth = 0;
189     auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
190                                           mapperIntf, "GetSubTree");
191     mapperCall.append("/");
192     mapperCall.append(depth);
193     mapperCall.append(std::vector<Interface>({logIntf}));
194 
195     try
196     {
197         auto mapperResponseMsg = bus.call(mapperCall);
198         mapperResponseMsg.read(subtree);
199     }
200     catch (const sdbusplus::exception_t& e)
201     {
202         lg2::error(
203             "Failed to parse existing callouts subtree message, ERROR = {ERROR}",
204             "ERROR", e);
205     }
206 }
207 
208 void Add::processExistingCallouts(sdbusplus::bus_t& bus)
209 {
210     MapperResponseType mapperResponse;
211 
212     getLoggingSubTree(bus, mapperResponse);
213     if (mapperResponse.empty())
214     {
215         // No errors to process.
216         return;
217     }
218 
219     for (const auto& elem : mapperResponse)
220     {
221         auto method = bus.new_method_call(
222             elem.second.begin()->first.c_str(), elem.first.c_str(),
223             "org.freedesktop.DBus.Properties", "Get");
224         method.append("xyz.openbmc_project.Association.Definitions");
225         method.append("Associations");
226         auto reply = bus.call(method);
227         if (reply.is_method_error())
228         {
229             // do not stop, continue with next elog
230             lg2::error("Error in getting associations");
231             continue;
232         }
233 
234         std::variant<AssociationList> assoc;
235         try
236         {
237             reply.read(assoc);
238         }
239         catch (const sdbusplus::exception_t& e)
240         {
241             lg2::error(
242                 "Failed to parse existing callouts associations message, ERROR = {ERROR}",
243                 "ERROR", e);
244             continue;
245         }
246         auto& assocs = std::get<AssociationList>(assoc);
247         if (assocs.empty())
248         {
249             // no associations, skip
250             continue;
251         }
252 
253         for (const auto& item : assocs)
254         {
255             if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
256             {
257                 removeWatches.emplace_back(
258                     std::make_unique<Remove>(bus, std::get<2>(item)));
259                 action(bus, std::get<2>(item), true);
260             }
261         }
262     }
263 }
264 
265 void Remove::removed(sdbusplus::message_t& msg)
266 {
267     auto bus = msg.get_bus();
268 
269     action(bus, inventoryPath, false);
270     return;
271 }
272 
273 } // namespace monitor
274 } // namespace fault
275 } // namespace fru
276 } // namespace led
277 } // namespace phosphor
278