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
getService(sdbusplus::bus_t & bus,const std::string & path)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
action(sdbusplus::bus_t & bus,const std::string & path,bool assert)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
created(sdbusplus::message_t & msg)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) == CALLOUT_REV_ASSOCIATION)
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
getLoggingSubTree(sdbusplus::bus_t & bus,MapperResponseType & subtree)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
processExistingCallouts(sdbusplus::bus_t & bus)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) == CALLOUT_REV_ASSOCIATION)
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
removed(sdbusplus::message_t & msg)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