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