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     static std::unique_ptr<sdbusplus::bus::match::match> match = nullptr;
183 
184     std::function<void(sdbusplus::message::message&)> thresholdCallback =
185         [&conn](sdbusplus::message::message& message) {
186             std::string objectName;
187             boost::container::flat_map<std::string, std::variant<bool>> values;
188             message.read(objectName, values);
189 
190             if constexpr (debug)
191             {
192                 std::cerr << "Threshold callback " << message.get_path()
193                           << "\n";
194             }
195 
196             auto findCriticalLow = values.find("CriticalAlarmLow");
197             auto findCriticalHigh = values.find("CriticalAlarmHigh");
198 
199             auto findWarnLow = values.find("WarningAlarmLow");
200             auto findWarnHigh = values.find("WarningAlarmHigh");
201 
202             if (findCriticalLow != values.end())
203             {
204                 criticalAssertMap[message.get_path()]["Low"] =
205                     std::get<bool>(findCriticalLow->second);
206             }
207             if (findCriticalHigh != values.end())
208             {
209                 criticalAssertMap[message.get_path()]["High"] =
210                     std::get<bool>(findCriticalHigh->second);
211             }
212             if (findWarnLow != values.end())
213             {
214                 warningAssertMap[message.get_path()]["Low"] =
215                     std::get<bool>(findWarnLow->second);
216             }
217             if (findWarnHigh != values.end())
218             {
219                 warningAssertMap[message.get_path()]["High"] =
220                     std::get<bool>(findWarnHigh->second);
221             }
222             updateLedStatus(conn);
223         };
224 
225     match = std::make_unique<sdbusplus::bus::match::match>(
226         static_cast<sdbusplus::bus::bus&>(*conn),
227         "type='signal',interface='org.freedesktop.DBus.Properties',"
228         "path_"
229         "namespace='/xyz/openbmc_project/"
230         "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
231         thresholdCallback);
232 }
233 
234 int main(int argc, char** argv)
235 {
236     boost::asio::io_service io;
237     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
238     conn->request_name("xyz.openbmc_project.CallbackManager");
239     sdbusplus::asio::object_server objServer(conn);
240     std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
241         objServer.add_interface(rootPath,
242                                 "xyz.openbmc_project.CallbackManager");
243     rootIface->register_method("RetriggerLEDUpdate",
244                                [&conn]() { updateLedStatus(conn, true); });
245     rootIface->initialize();
246 
247     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
248         objServer.add_interface(rootPath, globalInventoryIface);
249     inventoryIface->initialize();
250 
251     associationManager = std::make_unique<AssociationManager>(objServer, conn);
252 
253     createThresholdMatch(conn);
254     updateLedStatus(conn);
255 
256     io.run();
257 
258     return 0;
259 }
260