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