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         }
188     }
189 
190     for (const auto& ledPair : ledsToSet)
191     {
192         conn->async_method_call(
193             [ledPair](const boost::system::error_code ec) {
194                 if (ec)
195                 {
196                     std::cerr << "Cannot set " << ledPair.first << " to "
197                               << std::boolalpha
198                               << std::get<bool>(ledPair.second) << "\n";
199                 }
200                 if constexpr (debug)
201                 {
202                     std::cerr << "Set " << ledPair.first << " to "
203                               << std::boolalpha
204                               << std::get<bool>(ledPair.second) << "\n";
205                 }
206             },
207             ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
208             "Set", ledIface, ledAssertProp, ledPair.second);
209     }
210 }
211 
createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)212 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
213 {
214 
215     static sdbusplus::bus::match_t match(
216         static_cast<sdbusplus::bus_t&>(*conn),
217         "type='signal',member='ThresholdAsserted'",
218         [&conn](sdbusplus::message_t& message) {
219             std::string sensorName;
220             std::string thresholdInterface;
221             std::string event;
222             bool assert;
223             double assertValue;
224 
225             try
226             {
227                 message.read(sensorName, thresholdInterface, event, assert,
228                              assertValue);
229             }
230             catch (sdbusplus::exception_t&)
231             {
232                 return;
233             }
234             if constexpr (debug)
235             {
236                 std::cerr << "Threshold callback: SensorName = " << sensorName
237                           << ", Event = " << event << ", Asserted = " << assert
238                           << "\n";
239             }
240 
241             if (event == "CriticalAlarmLow")
242             {
243                 criticalAssertMap[message.get_path()]["low"] = assert;
244             }
245             else if (event == "CriticalAlarmHigh")
246             {
247                 criticalAssertMap[message.get_path()]["high"] = assert;
248             }
249             else if (event == "WarningAlarmLow")
250             {
251                 warningAssertMap[message.get_path()]["low"] = assert;
252             }
253             else if (event == "WarningAlarmHigh")
254             {
255                 warningAssertMap[message.get_path()]["high"] = assert;
256             }
257 
258             associationManager->setSensorAssociations(
259                 assertedInMap(criticalAssertMap),
260                 assertedInMap(warningAssertMap));
261 
262             updateLedStatus(conn);
263         });
264 }
265 
createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)266 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
267 {
268     static sdbusplus::bus::match_t match(
269         static_cast<sdbusplus::bus_t&>(*conn),
270         "type='signal',interface='org.freedesktop.DBus.Properties',"
271         "arg0namespace='" +
272             std::string(associationIface) + "'",
273         [&conn](sdbusplus::message_t& message) {
274             if (message.get_path() == rootPath)
275             {
276                 return; // it's us
277             }
278             std::string objectName;
279             boost::container::flat_map<std::string,
280                                        std::variant<std::vector<Association>>>
281                 values;
282             try
283             {
284                 message.read(objectName, values);
285             }
286             catch (sdbusplus::exception_t&)
287             {
288                 return;
289             }
290 
291             if constexpr (debug)
292             {
293                 std::cerr << "Association callback " << message.get_path()
294                           << "\n";
295             }
296 
297             auto findAssociations = values.find("Associations");
298             if (findAssociations == values.end())
299             {
300                 return;
301             }
302             const std::vector<Association>* associations =
303                 std::get_if<std::vector<Association>>(
304                     &findAssociations->second);
305 
306             if (associations == nullptr)
307             {
308                 std::cerr << "Illegal Association on " << message.get_path()
309                           << "\n";
310                 return;
311             }
312 
313             bool localWarning = false;
314             bool localCritical = false;
315             bool globalWarning = false;
316             bool globalCritical = false;
317 
318             for (const auto& [forward, reverse, path] : *associations)
319             {
320                 if (path == rootPath)
321                 {
322                     globalWarning = globalWarning ? true : reverse == "warning";
323                     globalCritical =
324                         globalCritical ? true : reverse == "critical";
325 
326                     if constexpr (1)
327                     {
328                         std::cerr << "got global ";
329                     }
330                 }
331                 else
332                 {
333                     localWarning = localWarning ? true : reverse == "warning";
334                     localCritical =
335                         localCritical ? true : reverse == "critical";
336                 }
337                 if (globalCritical && localCritical)
338                 {
339                     break;
340                 }
341             }
342 
343             bool fatal = globalCritical && localCritical;
344             bool critical = globalWarning && localCritical;
345             bool warning = globalWarning && !critical;
346 
347             fatalAssertMap[message.get_path()]["association"] = fatal;
348             criticalAssertMap[message.get_path()]["association"] = critical;
349             warningAssertMap[message.get_path()]["association"] = warning;
350 
351             updateLedStatus(conn);
352         });
353 }
354 
main(int argc,char ** argv)355 int main(int argc, char** argv)
356 {
357     boost::asio::io_context io;
358     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
359     conn->request_name("xyz.openbmc_project.CallbackManager");
360     sdbusplus::asio::object_server objServer(conn);
361     std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
362         objServer.add_interface(rootPath,
363                                 "xyz.openbmc_project.CallbackManager");
364     rootIface->register_method("RetriggerLEDUpdate",
365                                [&conn]() { updateLedStatus(conn, true); });
366     rootIface->initialize();
367 
368     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
369         objServer.add_interface(rootPath, globalInventoryIface);
370     inventoryIface->initialize();
371 
372     associationManager = std::make_unique<AssociationManager>(objServer, conn);
373 
374     createThresholdMatch(conn);
375     createAssociationMatch(conn);
376     updateLedStatus(conn);
377 
378     io.run();
379 
380     return 0;
381 }
382