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