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