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_service.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 
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 
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 
103     if (fatal)
104     {
105         currentPriority = StatusSetting::fatal;
106     }
107     else if (critical)
108     {
109         currentPriority = StatusSetting::critical;
110     }
111     else if (warn)
112     {
113         currentPriority = StatusSetting::warn;
114     }
115     else
116     {
117         currentPriority = StatusSetting::ok;
118     }
119 
120     std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
121 
122     if (last != currentPriority || forceRefresh)
123     {
124         switch (currentPriority)
125         {
126             case (StatusSetting::fatal):
127             {
128                 ledsToSet.push_back(std::make_pair(fatalLedPath, true));
129                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
130                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
131                 ledsToSet.push_back(std::make_pair(okLedPath, false));
132                 break;
133             }
134             case (StatusSetting::critical):
135             {
136                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
137                 ledsToSet.push_back(std::make_pair(criticalLedPath, true));
138                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
139                 ledsToSet.push_back(std::make_pair(okLedPath, false));
140                 break;
141             }
142             case (StatusSetting::warn):
143             {
144                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
145                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
146                 ledsToSet.push_back(std::make_pair(warningLedPath, true));
147                 ledsToSet.push_back(std::make_pair(okLedPath, false));
148                 break;
149             }
150             case (StatusSetting::ok):
151             {
152                 ledsToSet.push_back(std::make_pair(fatalLedPath, false));
153                 ledsToSet.push_back(std::make_pair(criticalLedPath, false));
154                 ledsToSet.push_back(std::make_pair(warningLedPath, false));
155                 ledsToSet.push_back(std::make_pair(okLedPath, true));
156                 break;
157             }
158         }
159     }
160 
161     for (const auto& ledPair : ledsToSet)
162     {
163         conn->async_method_call(
164             [ledPair](const boost::system::error_code ec) {
165                 if (ec)
166                 {
167                     std::cerr << "Cannot set " << ledPair.first << " to "
168                               << std::boolalpha
169                               << std::get<bool>(ledPair.second) << "\n";
170                 }
171                 if constexpr (debug)
172                 {
173                     std::cerr << "Set " << ledPair.first << " to "
174                               << std::boolalpha
175                               << std::get<bool>(ledPair.second) << "\n";
176                 }
177             },
178             ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
179             "Set", ledIface, ledAssertProp, ledPair.second);
180     }
181 }
182 
183 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
184 {
185 
186     static sdbusplus::bus::match::match match(
187         static_cast<sdbusplus::bus::bus&>(*conn),
188         "type='signal',interface='org.freedesktop.DBus.Properties',"
189         "path_"
190         "namespace='/xyz/openbmc_project/"
191         "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
192         [&conn](sdbusplus::message::message& message) {
193             std::string objectName;
194             boost::container::flat_map<std::string, std::variant<bool>> values;
195 
196             try
197             {
198                 message.read(objectName, values);
199             }
200             catch (sdbusplus::exception_t&)
201             {
202                 return;
203             }
204             if constexpr (debug)
205             {
206                 std::cerr << "Threshold callback " << message.get_path()
207                           << "\n";
208             }
209 
210             auto findCriticalLow = values.find("CriticalAlarmLow");
211             auto findCriticalHigh = values.find("CriticalAlarmHigh");
212 
213             auto findWarnLow = values.find("WarningAlarmLow");
214             auto findWarnHigh = values.find("WarningAlarmHigh");
215 
216             if (findCriticalLow != values.end())
217             {
218                 criticalAssertMap[message.get_path()]["Low"] =
219                     std::get<bool>(findCriticalLow->second);
220             }
221             if (findCriticalHigh != values.end())
222             {
223                 criticalAssertMap[message.get_path()]["High"] =
224                     std::get<bool>(findCriticalHigh->second);
225             }
226             if (findWarnLow != values.end())
227             {
228                 warningAssertMap[message.get_path()]["Low"] =
229                     std::get<bool>(findWarnLow->second);
230             }
231             if (findWarnHigh != values.end())
232             {
233                 warningAssertMap[message.get_path()]["High"] =
234                     std::get<bool>(findWarnHigh->second);
235             }
236 
237             associationManager->setSensorAssociations(
238                 assertedInMap(criticalAssertMap),
239                 assertedInMap(warningAssertMap));
240 
241             updateLedStatus(conn);
242         });
243 }
244 
245 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
246 {
247     static sdbusplus::bus::match::match match(
248         static_cast<sdbusplus::bus::bus&>(*conn),
249         "type='signal',interface='org.freedesktop.DBus.Properties',"
250         "arg0namespace='" +
251             std::string(associationIface) + "'",
252         [&conn](sdbusplus::message::message& message) {
253             if (message.get_path() == rootPath)
254             {
255                 return; // it's us
256             }
257             std::string objectName;
258             boost::container::flat_map<std::string,
259                                        std::variant<std::vector<Association>>>
260                 values;
261             try
262             {
263                 message.read(objectName, values);
264             }
265             catch (sdbusplus::exception_t&)
266             {
267                 return;
268             }
269 
270             if constexpr (debug)
271             {
272                 std::cerr << "Association callback " << message.get_path()
273                           << "\n";
274             }
275 
276             auto findAssociations = values.find("Associations");
277             if (findAssociations == values.end())
278             {
279                 return;
280             }
281             const std::vector<Association>* associations =
282                 std::get_if<std::vector<Association>>(
283                     &findAssociations->second);
284 
285             if (associations == nullptr)
286             {
287                 std::cerr << "Illegal Association on " << message.get_path()
288                           << "\n";
289                 return;
290             }
291 
292             bool localWarning = false;
293             bool localCritical = false;
294             bool globalWarning = false;
295             bool globalCritical = false;
296 
297             for (const auto& [forward, reverse, path] : *associations)
298             {
299                 if (path == rootPath)
300                 {
301                     globalWarning = globalWarning ? true : reverse == "warning";
302                     globalCritical =
303                         globalCritical ? true : reverse == "critical";
304 
305                     if constexpr (1)
306                     {
307                         std::cerr << "got global ";
308                     }
309                 }
310                 else
311                 {
312                     localWarning = localWarning ? true : reverse == "warning";
313                     localCritical =
314                         localCritical ? true : reverse == "critical";
315                 }
316                 if (globalCritical && localCritical)
317                 {
318                     break;
319                 }
320             }
321 
322             bool fatal = globalCritical && localCritical;
323             bool critical = globalWarning && localCritical;
324             bool warning = globalWarning && !critical;
325 
326             fatalAssertMap[message.get_path()]["association"] = fatal;
327             criticalAssertMap[message.get_path()]["association"] = critical;
328             warningAssertMap[message.get_path()]["association"] = warning;
329 
330             updateLedStatus(conn);
331         });
332 }
333 
334 int main(int argc, char** argv)
335 {
336     boost::asio::io_service io;
337     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
338     conn->request_name("xyz.openbmc_project.CallbackManager");
339     sdbusplus::asio::object_server objServer(conn);
340     std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
341         objServer.add_interface(rootPath,
342                                 "xyz.openbmc_project.CallbackManager");
343     rootIface->register_method("RetriggerLEDUpdate",
344                                [&conn]() { updateLedStatus(conn, true); });
345     rootIface->initialize();
346 
347     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
348         objServer.add_interface(rootPath, globalInventoryIface);
349     inventoryIface->initialize();
350 
351     associationManager = std::make_unique<AssociationManager>(objServer, conn);
352 
353     createThresholdMatch(conn);
354     createAssociationMatch(conn);
355     updateLedStatus(conn);
356 
357     io.run();
358 
359     return 0;
360 }
361