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