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