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 <boost/container/flat_map.hpp>
18 #include <iostream>
19 #include <sdbusplus/asio/connection.hpp>
20 #include <sdbusplus/asio/object_server.hpp>
21 #include <variant>
22 
23 constexpr const char* fatalLedPath =
24     "/xyz/openbmc_project/led/groups/status_critical";
25 constexpr const char* criticalLedPath =
26     "/xyz/openbmc_project/led/groups/status_non_critical";
27 constexpr const char* warningLedPath =
28     "/xyz/openbmc_project/led/groups/status_degraded";
29 constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
30 
31 constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
32 constexpr const char* ledAssertProp = "Asserted";
33 constexpr const char* ledManagerBusname =
34     "xyz.openbmc_project.LED.GroupManager";
35 
36 enum class StatusSetting
37 {
38     none,
39     ok,
40     warn,
41     critical,
42     fatal
43 };
44 
45 std::shared_ptr<sdbusplus::asio::dbus_interface> assertedIface = nullptr;
46 
47 constexpr const bool debug = false;
48 
49 // final led state tracking
50 StatusSetting currentPriority = StatusSetting::none;
51 
52 // maps of <object-path, <property, asserted>>
53 boost::container::flat_map<std::string,
54                            boost::container::flat_map<std::string, bool>>
55     fatalAssertMap;
56 boost::container::flat_map<std::string,
57                            boost::container::flat_map<std::string, bool>>
58     criticalAssertMap;
59 boost::container::flat_map<std::string,
60                            boost::container::flat_map<std::string, bool>>
61     warningAssertMap;
62 
63 std::vector<std::string> assertedInMap(
64     const boost::container::flat_map<
65         std::string, boost::container::flat_map<std::string, bool>>& map)
66 {
67     std::vector<std::string> ret;
68     // if any of the properties are true, return true
69     for (const auto& pair : map)
70     {
71         for (const auto& item : pair.second)
72         {
73             if (item.second)
74             {
75                 ret.push_back(pair.first);
76             }
77         }
78     }
79     return ret;
80 }
81 
82 void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn,
83                      bool forceRefresh = false)
84 {
85     std::vector<std::string> assertedVector = assertedInMap(fatalAssertMap);
86     assertedIface->set_property("Fatal", assertedVector);
87     bool fatal = assertedVector.size();
88 
89     assertedVector = assertedInMap(criticalAssertMap);
90     assertedIface->set_property("Critical", assertedVector);
91     bool critical = assertedVector.size();
92 
93     assertedVector = assertedInMap(warningAssertMap);
94     assertedIface->set_property("Warning", assertedVector);
95 
96     bool warn = assertedVector.size();
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     assertedIface =
241         objServer.add_interface("/xyz/openbmc_project/CallbackManager",
242                                 "xyz.openbmc_project.CallbackManager");
243     assertedIface->register_property("Warning", std::vector<std::string>());
244     assertedIface->register_property("Critical", std::vector<std::string>());
245     assertedIface->register_property("Fatal", std::vector<std::string>());
246     assertedIface->register_method("RetriggerLEDUpdate",
247                                    [&conn]() { updateLedStatus(conn, true); });
248     assertedIface->initialize();
249 
250     createThresholdMatch(conn);
251     updateLedStatus(conn);
252 
253     io.run();
254 
255     return 0;
256 }
257