xref: /openbmc/s2600wf-misc/subprojects/callback-manager/src/callback_manager.cpp (revision 69ae808e213f2e3ce5a0fc7e805820026065064c)
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/asio/io_context.hpp>
20 #include <boost/asio/steady_timer.hpp>
21 #include <boost/container/flat_map.hpp>
22 #include <sdbusplus/asio/connection.hpp>
23 #include <sdbusplus/asio/object_server.hpp>
24 
25 #include <variant>
26 
27 constexpr const char* fatalLedPath =
28     "/xyz/openbmc_project/led/groups/status_critical";
29 constexpr const char* criticalLedPath =
30     "/xyz/openbmc_project/led/groups/status_non_critical";
31 constexpr const char* warningLedPath =
32     "/xyz/openbmc_project/led/groups/status_degraded";
33 constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
34 
35 constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
36 constexpr const char* ledAssertProp = "Asserted";
37 constexpr const char* ledManagerBusname =
38     "xyz.openbmc_project.LED.GroupManager";
39 
40 std::unique_ptr<AssociationManager> associationManager;
41 
42 enum class StatusSetting
43 {
44     none,
45     ok,
46     warn,
47     critical,
48     fatal
49 };
50 
51 constexpr const bool debug = false;
52 
53 // final led state tracking
54 StatusSetting currentPriority = StatusSetting::none;
55 
56 // maps of <object-path, <property, asserted>>
57 boost::container::flat_map<std::string,
58                            boost::container::flat_map<std::string, bool>>
59     fatalAssertMap;
60 boost::container::flat_map<std::string,
61                            boost::container::flat_map<std::string, bool>>
62     criticalAssertMap;
63 boost::container::flat_map<std::string,
64                            boost::container::flat_map<std::string, bool>>
65     warningAssertMap;
66 
assertedInMap(const boost::container::flat_map<std::string,boost::container::flat_map<std::string,bool>> & map)67 std::vector<std::string> assertedInMap(
68     const boost::container::flat_map<
69         std::string, boost::container::flat_map<std::string, bool>>& map)
70 {
71     std::vector<std::string> ret;
72     // if any of the properties are true, return true
73     for (const auto& pair : map)
74     {
75         for (const auto& item : pair.second)
76         {
77             if (item.second)
78             {
79                 ret.push_back(pair.first);
80             }
81         }
82     }
83     return ret;
84 }
85 
updateLedStatus(std::shared_ptr<sdbusplus::asio::connection> & conn,bool forceRefresh=false)86 void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn,
87                      bool forceRefresh = false)
88 {
89     std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap);
90     bool fatal = fatalVector.size();
91 
92     std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap);
93     bool critical = criticalVector.size();
94 
95     std::vector<std::string> warningVector = assertedInMap(warningAssertMap);
96     bool warn = warningVector.size();
97 
98     associationManager->setLocalAssociations(fatalVector, criticalVector,
99                                              warningVector);
100 
101     StatusSetting last = currentPriority;
102     std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
103     if (forceRefresh)
104     {
105         ledsToSet.push_back(std::make_pair(fatalLedPath, false));
106         ledsToSet.push_back(std::make_pair(criticalLedPath, false));
107         ledsToSet.push_back(std::make_pair(warningLedPath, false));
108         ledsToSet.push_back(std::make_pair(okLedPath, false));
109         for (const auto& ledPair : ledsToSet)
110         {
111             conn->async_method_call(
112                 [ledPair](const boost::system::error_code ec) {
113                     std::ios_base::fmtflags originalFlags = std::cerr.flags();
114                     if (ec)
115                     {
116                         std::cerr << "Cannot set " << ledPair.first << " to "
117                                   << std::boolalpha
118                                   << std::get<bool>(ledPair.second) << "\n";
119                         std::cerr.flags(originalFlags);
120                     }
121                     if constexpr (debug)
122                     {
123                         std::cerr << "Set " << ledPair.first << " to "
124                                   << std::boolalpha
125                                   << std::get<bool>(ledPair.second) << "\n";
126                         std::cerr.flags(originalFlags);
127                     }
128                 },
129                 ledManagerBusname, ledPair.first,
130                 "org.freedesktop.DBus.Properties", "Set", ledIface,
131                 ledAssertProp, ledPair.second);
132         }
133     }
134     if (fatal)
135     {
136         currentPriority = StatusSetting::fatal;
137     }
138     else if (critical)
139     {
140         currentPriority = StatusSetting::critical;
141     }
142     else if (warn)
143     {
144         currentPriority = StatusSetting::warn;
145     }
146     else
147     {
148         currentPriority = StatusSetting::ok;
149     }
150 
151     if (last != currentPriority || forceRefresh)
152     {
153         switch (currentPriority)
154         {
155             case (StatusSetting::fatal):
156             {
157                 ledsToSet.push_back(std::make_pair(fatalLedPath, true));
158                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
159                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
160                 ledsToSet.push_back(std::make_pair(okLedPath, false));
161                 break;
162             }
163             case (StatusSetting::critical):
164             {
165                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
166                 ledsToSet.push_back(std::make_pair(criticalLedPath, true));
167                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
168                 ledsToSet.push_back(std::make_pair(okLedPath, false));
169                 break;
170             }
171             case (StatusSetting::warn):
172             {
173                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
174                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
175                 ledsToSet.push_back(std::make_pair(warningLedPath, true));
176                 ledsToSet.push_back(std::make_pair(okLedPath, false));
177                 break;
178             }
179             case (StatusSetting::ok):
180             {
181                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
182                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
183                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
184                 ledsToSet.push_back(std::make_pair(okLedPath, true));
185                 break;
186             }
187             case (StatusSetting::none):
188             default:
189                 break;
190         }
191     }
192 
193     for (const auto& ledPair : ledsToSet)
194     {
195         conn->async_method_call(
196             [ledPair](const boost::system::error_code ec) {
197                 if (ec)
198                 {
199                     std::cerr << "Cannot set " << ledPair.first << " to "
200                               << std::boolalpha
201                               << std::get<bool>(ledPair.second) << "\n";
202                 }
203                 if constexpr (debug)
204                 {
205                     std::cerr
206                         << "Set " << ledPair.first << " to " << std::boolalpha
207                         << std::get<bool>(ledPair.second) << "\n";
208                 }
209             },
210             ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
211             "Set", ledIface, ledAssertProp, ledPair.second);
212     }
213 }
214 
createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)215 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
216 {
217     static sdbusplus::bus::match_t match(
218         static_cast<sdbusplus::bus_t&>(*conn),
219         "type='signal',member='ThresholdAsserted'",
220         [&conn](sdbusplus::message_t& message) {
221             std::string sensorName;
222             std::string thresholdInterface;
223             std::string event;
224             bool assert;
225             double assertValue;
226 
227             try
228             {
229                 message.read(sensorName, thresholdInterface, event, assert,
230                              assertValue);
231             }
232             catch (sdbusplus::exception_t&)
233             {
234                 return;
235             }
236             if constexpr (debug)
237             {
238                 std::cerr << "Threshold callback: SensorName = " << sensorName
239                           << ", Event = " << event << ", Asserted = " << assert
240                           << "\n";
241             }
242 
243             if (event == "CriticalAlarmLow")
244             {
245                 criticalAssertMap[message.get_path()]["low"] = assert;
246             }
247             else if (event == "CriticalAlarmHigh")
248             {
249                 criticalAssertMap[message.get_path()]["high"] = assert;
250             }
251             else if (event == "WarningAlarmLow")
252             {
253                 warningAssertMap[message.get_path()]["low"] = assert;
254             }
255             else if (event == "WarningAlarmHigh")
256             {
257                 warningAssertMap[message.get_path()]["high"] = assert;
258             }
259 
260             associationManager->setSensorAssociations(
261                 assertedInMap(criticalAssertMap),
262                 assertedInMap(warningAssertMap));
263 
264             updateLedStatus(conn);
265         });
266 }
267 
createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)268 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
269 {
270     static sdbusplus::bus::match_t match(
271         static_cast<sdbusplus::bus_t&>(*conn),
272         "type='signal',interface='org.freedesktop.DBus.Properties',"
273         "arg0namespace='" +
274             std::string(associationIface) + "'",
275         [&conn](sdbusplus::message_t& message) {
276             if (message.get_path() == rootPath)
277             {
278                 return; // it's us
279             }
280             std::string objectName;
281             boost::container::flat_map<std::string,
282                                        std::variant<std::vector<Association>>>
283                 values;
284             try
285             {
286                 message.read(objectName, values);
287             }
288             catch (sdbusplus::exception_t&)
289             {
290                 return;
291             }
292 
293             if constexpr (debug)
294             {
295                 std::cerr << "Association callback " << message.get_path()
296                           << "\n";
297             }
298 
299             auto findAssociations = values.find("Associations");
300             if (findAssociations == values.end())
301             {
302                 return;
303             }
304             const std::vector<Association>* associations =
305                 std::get_if<std::vector<Association>>(
306                     &findAssociations->second);
307 
308             if (associations == nullptr)
309             {
310                 std::cerr << "Illegal Association on " << message.get_path()
311                           << "\n";
312                 return;
313             }
314 
315             bool localWarning = false;
316             bool localCritical = false;
317             bool globalWarning = false;
318             bool globalCritical = false;
319 
320             for (const auto& [forward, reverse, path] : *associations)
321             {
322                 if (path == rootPath)
323                 {
324                     globalWarning = globalWarning ? true : reverse == "warning";
325                     globalCritical =
326                         globalCritical ? true : reverse == "critical";
327 
328                     if constexpr (1)
329                     {
330                         std::cerr << "got global ";
331                     }
332                 }
333                 else
334                 {
335                     localWarning = localWarning ? true : reverse == "warning";
336                     localCritical =
337                         localCritical ? true : reverse == "critical";
338                 }
339                 if (globalCritical && localCritical)
340                 {
341                     break;
342                 }
343             }
344 
345             bool fatal = globalCritical && localCritical;
346             bool critical = globalWarning && localCritical;
347             bool warning = globalWarning && !critical;
348 
349             fatalAssertMap[message.get_path()]["association"] = fatal;
350             criticalAssertMap[message.get_path()]["association"] = critical;
351             warningAssertMap[message.get_path()]["association"] = warning;
352 
353             updateLedStatus(conn);
354         });
355 }
356 
main(int,char **)357 int main(int /*argc*/, char** /*argv*/)
358 {
359     boost::asio::io_context io;
360     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
361     conn->request_name("xyz.openbmc_project.CallbackManager");
362     sdbusplus::asio::object_server objServer(conn);
363     std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
364         objServer.add_interface(rootPath,
365                                 "xyz.openbmc_project.CallbackManager");
366     rootIface->register_method("RetriggerLEDUpdate", [&conn]() {
367         updateLedStatus(conn, true);
368     });
369     rootIface->initialize();
370 
371     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
372         objServer.add_interface(rootPath, globalInventoryIface);
373     inventoryIface->initialize();
374 
375     associationManager = std::make_unique<AssociationManager>(objServer, conn);
376 
377     createThresholdMatch(conn);
378     createAssociationMatch(conn);
379     updateLedStatus(conn);
380 
381     io.run();
382 
383     return 0;
384 }
385