xref: /openbmc/s2600wf-misc/callback-manager/src/callback_manager.cpp (revision 56078401443df160653badb456e37cec2baca0f5)
1 /*
2 // Copyright (c) 2019 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "callback_manager.hpp"
18 
19 #include <boost/container/flat_map.hpp>
20 #include <sdbusplus/asio/connection.hpp>
21 #include <sdbusplus/asio/object_server.hpp>
22 
23 #include <variant>
24 
25 constexpr const char* fatalLedPath =
26     "/xyz/openbmc_project/led/groups/status_critical";
27 constexpr const char* criticalLedPath =
28     "/xyz/openbmc_project/led/groups/status_non_critical";
29 constexpr const char* warningLedPath =
30     "/xyz/openbmc_project/led/groups/status_degraded";
31 constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
32 
33 constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
34 constexpr const char* ledAssertProp = "Asserted";
35 constexpr const char* ledManagerBusname =
36     "xyz.openbmc_project.LED.GroupManager";
37 
38 std::unique_ptr<AssociationManager> associationManager;
39 
40 enum class StatusSetting
41 {
42     none,
43     ok,
44     warn,
45     critical,
46     fatal
47 };
48 
49 constexpr const bool debug = false;
50 
51 // final led state tracking
52 StatusSetting currentPriority = StatusSetting::none;
53 
54 // maps of <object-path, <property, asserted>>
55 boost::container::flat_map<std::string,
56                            boost::container::flat_map<std::string, bool>>
57     fatalAssertMap;
58 boost::container::flat_map<std::string,
59                            boost::container::flat_map<std::string, bool>>
60     criticalAssertMap;
61 boost::container::flat_map<std::string,
62                            boost::container::flat_map<std::string, bool>>
63     warningAssertMap;
64 
65 std::vector<std::string> assertedInMap(
66     const boost::container::flat_map<
67         std::string, boost::container::flat_map<std::string, bool>>& map)
68 {
69     std::vector<std::string> ret;
70     // if any of the properties are true, return true
71     for (const auto& pair : map)
72     {
73         for (const auto& item : pair.second)
74         {
75             if (item.second)
76             {
77                 ret.push_back(pair.first);
78             }
79         }
80     }
81     return ret;
82 }
83 
84 void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn,
85                      bool forceRefresh = false)
86 {
87     std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap);
88     bool fatal = fatalVector.size();
89 
90     std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap);
91     bool critical = criticalVector.size();
92 
93     std::vector<std::string> warningVector = assertedInMap(warningAssertMap);
94     bool warn = warningVector.size();
95 
96     associationManager->setLocalAssociations(fatalVector, criticalVector,
97                                              warningVector);
98 
99     StatusSetting last = currentPriority;
100 
101     if (fatal)
102     {
103         currentPriority = StatusSetting::fatal;
104     }
105     else if (critical)
106     {
107         currentPriority = StatusSetting::critical;
108     }
109     else if (warn)
110     {
111         currentPriority = StatusSetting::warn;
112     }
113     else
114     {
115         currentPriority = StatusSetting::ok;
116     }
117 
118     std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
119 
120     if (last != currentPriority || forceRefresh)
121     {
122         switch (currentPriority)
123         {
124             case (StatusSetting::fatal):
125             {
126                 ledsToSet.push_back(std::make_pair(fatalLedPath, true));
127                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
128                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
129                 ledsToSet.push_back(std::make_pair(okLedPath, false));
130                 break;
131             }
132             case (StatusSetting::critical):
133             {
134                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
135                 ledsToSet.push_back(std::make_pair(criticalLedPath, true));
136                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
137                 ledsToSet.push_back(std::make_pair(okLedPath, false));
138                 break;
139             }
140             case (StatusSetting::warn):
141             {
142                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
143                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
144                 ledsToSet.push_back(std::make_pair(warningLedPath, true));
145                 ledsToSet.push_back(std::make_pair(okLedPath, false));
146                 break;
147             }
148             case (StatusSetting::ok):
149             {
150                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
151                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
152                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
153                 ledsToSet.push_back(std::make_pair(okLedPath, true));
154                 break;
155             }
156         }
157     }
158 
159     for (const auto& ledPair : ledsToSet)
160     {
161         conn->async_method_call(
162             [ledPair](const boost::system::error_code ec) {
163                 if (ec)
164                 {
165                     std::cerr << "Cannot set " << ledPair.first << " to "
166                               << std::boolalpha
167                               << std::get<bool>(ledPair.second) << "\n";
168                 }
169                 if constexpr (debug)
170                 {
171                     std::cerr << "Set " << ledPair.first << " to "
172                               << std::boolalpha
173                               << std::get<bool>(ledPair.second) << "\n";
174                 }
175             },
176             ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
177             "Set", ledIface, ledAssertProp, ledPair.second);
178     }
179 }
180 
181 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
182 {
183 
184     static sdbusplus::bus::match::match match(
185         static_cast<sdbusplus::bus::bus&>(*conn),
186         "type='signal',interface='org.freedesktop.DBus.Properties',"
187         "path_"
188         "namespace='/xyz/openbmc_project/"
189         "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
190         [&conn](sdbusplus::message::message& message) {
191             std::string objectName;
192             boost::container::flat_map<std::string, std::variant<bool>> values;
193 
194             try
195             {
196                 message.read(objectName, values);
197             }
198             catch (sdbusplus::exception_t&)
199             {
200                 return;
201             }
202             if constexpr (debug)
203             {
204                 std::cerr << "Threshold callback " << message.get_path()
205                           << "\n";
206             }
207 
208             auto findCriticalLow = values.find("CriticalAlarmLow");
209             auto findCriticalHigh = values.find("CriticalAlarmHigh");
210 
211             auto findWarnLow = values.find("WarningAlarmLow");
212             auto findWarnHigh = values.find("WarningAlarmHigh");
213 
214             if (findCriticalLow != values.end())
215             {
216                 criticalAssertMap[message.get_path()]["Low"] =
217                     std::get<bool>(findCriticalLow->second);
218             }
219             if (findCriticalHigh != values.end())
220             {
221                 criticalAssertMap[message.get_path()]["High"] =
222                     std::get<bool>(findCriticalHigh->second);
223             }
224             if (findWarnLow != values.end())
225             {
226                 warningAssertMap[message.get_path()]["Low"] =
227                     std::get<bool>(findWarnLow->second);
228             }
229             if (findWarnHigh != values.end())
230             {
231                 warningAssertMap[message.get_path()]["High"] =
232                     std::get<bool>(findWarnHigh->second);
233             }
234 
235             associationManager->setSensorAssociations(
236                 assertedInMap(criticalAssertMap),
237                 assertedInMap(warningAssertMap));
238 
239             updateLedStatus(conn);
240         });
241 }
242 
243 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
244 {
245     static sdbusplus::bus::match::match match(
246         static_cast<sdbusplus::bus::bus&>(*conn),
247         "type='signal',interface='org.freedesktop.DBus.Properties',"
248         "arg0namespace='" +
249             std::string(associationIface) + "'",
250         [&conn](sdbusplus::message::message& message) {
251             if (message.get_path() == rootPath)
252             {
253                 return; // it's us
254             }
255             std::string objectName;
256             boost::container::flat_map<std::string,
257                                        std::variant<std::vector<Association>>>
258                 values;
259             try
260             {
261                 message.read(objectName, values);
262             }
263             catch (sdbusplus::exception_t&)
264             {
265                 return;
266             }
267 
268             if constexpr (debug)
269             {
270                 std::cerr << "Association callback " << message.get_path()
271                           << "\n";
272             }
273 
274             auto findAssociations = values.find("Associations");
275             if (findAssociations == values.end())
276             {
277                 return;
278             }
279             const std::vector<Association>* associations =
280                 std::get_if<std::vector<Association>>(
281                     &findAssociations->second);
282 
283             if (associations == nullptr)
284             {
285                 std::cerr << "Illegal Association on " << message.get_path()
286                           << "\n";
287                 return;
288             }
289 
290             bool localWarning = false;
291             bool localCritical = false;
292             bool globalWarning = false;
293             bool globalCritical = false;
294 
295             for (const auto& [forward, reverse, path] : *associations)
296             {
297                 if (path == rootPath)
298                 {
299                     globalWarning = globalWarning ? true : reverse == "warning";
300                     globalCritical =
301                         globalCritical ? true : reverse == "critical";
302 
303                     if constexpr (1)
304                     {
305                         std::cerr << "got global ";
306                     }
307                 }
308                 else
309                 {
310                     localWarning = localWarning ? true : reverse == "warning";
311                     localCritical =
312                         localCritical ? true : reverse == "critical";
313                 }
314                 if (globalCritical && localCritical)
315                 {
316                     break;
317                 }
318             }
319 
320             bool fatal = globalCritical && localCritical;
321             bool critical = globalWarning && localCritical;
322             bool warning = globalWarning && !critical;
323 
324             fatalAssertMap[message.get_path()]["association"] = fatal;
325             criticalAssertMap[message.get_path()]["association"] = critical;
326             warningAssertMap[message.get_path()]["association"] = warning;
327 
328             updateLedStatus(conn);
329         });
330 }
331 
332 int main(int argc, char** argv)
333 {
334     boost::asio::io_service io;
335     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
336     conn->request_name("xyz.openbmc_project.CallbackManager");
337     sdbusplus::asio::object_server objServer(conn);
338     std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
339         objServer.add_interface(rootPath,
340                                 "xyz.openbmc_project.CallbackManager");
341     rootIface->register_method("RetriggerLEDUpdate",
342                                [&conn]() { updateLedStatus(conn, true); });
343     rootIface->initialize();
344 
345     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
346         objServer.add_interface(rootPath, globalInventoryIface);
347     inventoryIface->initialize();
348 
349     associationManager = std::make_unique<AssociationManager>(objServer, conn);
350 
351     createThresholdMatch(conn);
352     createAssociationMatch(conn);
353     updateLedStatus(conn);
354 
355     io.run();
356 
357     return 0;
358 }
359