1 #include "fru-fault-monitor.hpp"
2 
3 #include "elog-errors.hpp"
4 
5 #include <phosphor-logging/elog.hpp>
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/exception.hpp>
8 #include <xyz/openbmc_project/Led/Fru/Monitor/error.hpp>
9 #include <xyz/openbmc_project/Led/Mapper/error.hpp>
10 
11 namespace phosphor
12 {
13 namespace led
14 {
15 namespace fru
16 {
17 namespace fault
18 {
19 namespace monitor
20 {
21 
22 using namespace phosphor::logging;
23 
24 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
25 constexpr auto MAPPER_OBJ_PATH = "/xyz/openbmc_project/object_mapper";
26 constexpr auto MAPPER_IFACE = "xyz.openbmc_project.ObjectMapper";
27 constexpr auto OBJMGR_IFACE = "org.freedesktop.DBus.ObjectManager";
28 constexpr auto LED_GROUPS = "/xyz/openbmc_project/led/groups/";
29 constexpr auto LOG_PATH = "/xyz/openbmc_project/logging";
30 constexpr auto LOG_IFACE = "xyz.openbmc_project.Logging.Entry";
31 
32 using AssociationList =
33     std::vector<std::tuple<std::string, std::string, std::string>>;
34 using Attributes = std::variant<bool, AssociationList>;
35 using PropertyName = std::string;
36 using PropertyMap = std::map<PropertyName, Attributes>;
37 using InterfaceName = std::string;
38 using InterfaceMap = std::map<InterfaceName, PropertyMap>;
39 
40 using Service = std::string;
41 using Path = std::string;
42 using Interface = std::string;
43 using Interfaces = std::vector<Interface>;
44 using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
45 
46 using MethodErr =
47     sdbusplus::xyz::openbmc_project::Led::Mapper::Error::MethodError;
48 using ObjectNotFoundErr =
49     sdbusplus::xyz::openbmc_project::Led::Mapper::Error::ObjectNotFoundError;
50 using InventoryPathErr = sdbusplus::xyz::openbmc_project::Led::Fru::Monitor::
51     Error::InventoryPathError;
52 
53 std::string getService(sdbusplus::bus::bus& bus, const std::string& path)
54 {
55     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
56                                       MAPPER_IFACE, "GetObject");
57     mapper.append(path.c_str(), std::vector<std::string>({OBJMGR_IFACE}));
58 
59     std::map<std::string, std::vector<std::string>> mapperResponse;
60     try
61     {
62         auto mapperResponseMsg = bus.call(mapper);
63         mapperResponseMsg.read(mapperResponse);
64     }
65     catch (const sdbusplus::exception::exception& e)
66     {
67         lg2::error(
68             "Failed to parse getService mapper response, ERROR = {ERROR}",
69             "ERROR", e);
70         using namespace xyz::openbmc_project::Led::Mapper;
71         elog<ObjectNotFoundErr>(ObjectNotFoundError::METHOD_NAME("GetObject"),
72                                 ObjectNotFoundError::PATH(path.c_str()),
73                                 ObjectNotFoundError::INTERFACE(OBJMGR_IFACE));
74     }
75     if (mapperResponse.empty())
76     {
77         using namespace xyz::openbmc_project::Led::Mapper;
78         elog<ObjectNotFoundErr>(ObjectNotFoundError::METHOD_NAME("GetObject"),
79                                 ObjectNotFoundError::PATH(path.c_str()),
80                                 ObjectNotFoundError::INTERFACE(OBJMGR_IFACE));
81     }
82 
83     return mapperResponse.cbegin()->first;
84 }
85 
86 void action(sdbusplus::bus::bus& bus, const std::string& path, bool assert)
87 {
88     std::string service;
89     try
90     {
91         std::string groups{LED_GROUPS};
92         groups.pop_back();
93         service = getService(bus, groups);
94     }
95     catch (MethodErr& e)
96     {
97         commit<MethodErr>();
98         return;
99     }
100     catch (ObjectNotFoundErr& e)
101     {
102         commit<ObjectNotFoundErr>();
103         return;
104     }
105 
106     auto pos = path.rfind("/");
107     if (pos == std::string::npos)
108     {
109         using namespace xyz::openbmc_project::Led::Fru::Monitor;
110         report<InventoryPathErr>(InventoryPathError::PATH(path.c_str()));
111         return;
112     }
113     auto unit = path.substr(pos + 1);
114 
115     std::string ledPath = LED_GROUPS + unit + '_' + LED_FAULT;
116 
117     auto method = bus.new_method_call(service.c_str(), ledPath.c_str(),
118                                       "org.freedesktop.DBus.Properties", "Set");
119     method.append("xyz.openbmc_project.Led.Group");
120     method.append("Asserted");
121 
122     method.append(std::variant<bool>(assert));
123 
124     try
125     {
126         bus.call_noreply(method);
127     }
128     catch (const sdbusplus::exception::exception& e)
129     {
130         // Log an info message, system may not have all the LED Groups defined
131         lg2::info("Failed to Assert LED Group, ERROR = {ERROR}", "ERROR", e);
132     }
133 
134     return;
135 }
136 
137 void Add::created(sdbusplus::message::message& msg)
138 {
139     auto bus = msg.get_bus();
140 
141     sdbusplus::message::object_path objectPath;
142     InterfaceMap interfaces;
143     try
144     {
145         msg.read(objectPath, interfaces);
146     }
147     catch (const sdbusplus::exception::exception& e)
148     {
149         lg2::error("Failed to parse created message, ERROR = {ERROR}", "ERROR",
150                    e);
151         return;
152     }
153 
154     std::size_t found = objectPath.str.find(ELOG_ENTRY);
155     if (found == std::string::npos)
156     {
157         // Not a new error entry skip
158         return;
159     }
160     auto iter = interfaces.find("xyz.openbmc_project.Association.Definitions");
161     if (iter == interfaces.end())
162     {
163         return;
164     }
165 
166     // Nothing else shows when a specific error log
167     // has been created. Do it here.
168     // TODO:(phosphor-logging#25): support sdbusplus::message::object_path
169     // directly.
170     lg2::info("{PATH} created", "PATH", objectPath.str);
171 
172     auto attr = iter->second.find("Associations");
173     if (attr == iter->second.end())
174     {
175         return;
176     }
177 
178     auto& assocs = std::get<AssociationList>(attr->second);
179     if (assocs.empty())
180     {
181         // No associations skip
182         return;
183     }
184 
185     for (const auto& item : assocs)
186     {
187         if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
188         {
189             removeWatches.emplace_back(
190                 std::make_unique<Remove>(bus, std::get<2>(item)));
191             action(bus, std::get<2>(item), true);
192         }
193     }
194 
195     return;
196 }
197 
198 void getLoggingSubTree(sdbusplus::bus::bus& bus, MapperResponseType& subtree)
199 {
200     auto depth = 0;
201     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
202                                           MAPPER_IFACE, "GetSubTree");
203     mapperCall.append("/");
204     mapperCall.append(depth);
205     mapperCall.append(std::vector<Interface>({LOG_IFACE}));
206 
207     try
208     {
209         auto mapperResponseMsg = bus.call(mapperCall);
210         mapperResponseMsg.read(subtree);
211     }
212     catch (const sdbusplus::exception::exception& e)
213     {
214         lg2::error(
215             "Failed to parse existing callouts subtree message, ERROR = {ERROR}",
216             "ERROR", e);
217     }
218 }
219 
220 void Add::processExistingCallouts(sdbusplus::bus::bus& bus)
221 {
222     MapperResponseType mapperResponse;
223 
224     getLoggingSubTree(bus, mapperResponse);
225     if (mapperResponse.empty())
226     {
227         // No errors to process.
228         return;
229     }
230 
231     for (const auto& elem : mapperResponse)
232     {
233         auto method = bus.new_method_call(
234             elem.second.begin()->first.c_str(), elem.first.c_str(),
235             "org.freedesktop.DBus.Properties", "Get");
236         method.append("xyz.openbmc_project.Association.Definitions");
237         method.append("Associations");
238         auto reply = bus.call(method);
239         if (reply.is_method_error())
240         {
241             // do not stop, continue with next elog
242             lg2::error("Error in getting associations");
243             continue;
244         }
245 
246         std::variant<AssociationList> assoc;
247         try
248         {
249             reply.read(assoc);
250         }
251         catch (const sdbusplus::exception::exception& e)
252         {
253             lg2::error(
254                 "Failed to parse existing callouts associations message, ERROR = {ERROR}",
255                 "ERROR", e);
256             continue;
257         }
258         auto& assocs = std::get<AssociationList>(assoc);
259         if (assocs.empty())
260         {
261             // no associations, skip
262             continue;
263         }
264 
265         for (const auto& item : assocs)
266         {
267             if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
268             {
269                 removeWatches.emplace_back(
270                     std::make_unique<Remove>(bus, std::get<2>(item)));
271                 action(bus, std::get<2>(item), true);
272             }
273         }
274     }
275 }
276 
277 void Remove::removed(sdbusplus::message::message& msg)
278 {
279     auto bus = msg.get_bus();
280 
281     action(bus, inventoryPath, false);
282     return;
283 }
284 
285 } // namespace monitor
286 } // namespace fault
287 } // namespace fru
288 } // namespace led
289 } // namespace phosphor
290