| /* |
| // Copyright (c) 2019 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| |
| #include "callback_manager.hpp" |
| |
| #include <boost/asio/io_service.hpp> |
| #include <boost/asio/steady_timer.hpp> |
| #include <boost/container/flat_map.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| |
| #include <variant> |
| |
| constexpr const char* fatalLedPath = |
| "/xyz/openbmc_project/led/groups/status_critical"; |
| constexpr const char* criticalLedPath = |
| "/xyz/openbmc_project/led/groups/status_non_critical"; |
| constexpr const char* warningLedPath = |
| "/xyz/openbmc_project/led/groups/status_degraded"; |
| constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok"; |
| |
| constexpr const char* ledIface = "xyz.openbmc_project.Led.Group"; |
| constexpr const char* ledAssertProp = "Asserted"; |
| constexpr const char* ledManagerBusname = |
| "xyz.openbmc_project.LED.GroupManager"; |
| |
| std::unique_ptr<AssociationManager> associationManager; |
| |
| enum class StatusSetting |
| { |
| none, |
| ok, |
| warn, |
| critical, |
| fatal |
| }; |
| |
| constexpr const bool debug = false; |
| |
| // final led state tracking |
| StatusSetting currentPriority = StatusSetting::none; |
| |
| // maps of <object-path, <property, asserted>> |
| boost::container::flat_map<std::string, |
| boost::container::flat_map<std::string, bool>> |
| fatalAssertMap; |
| boost::container::flat_map<std::string, |
| boost::container::flat_map<std::string, bool>> |
| criticalAssertMap; |
| boost::container::flat_map<std::string, |
| boost::container::flat_map<std::string, bool>> |
| warningAssertMap; |
| |
| std::vector<std::string> assertedInMap( |
| const boost::container::flat_map< |
| std::string, boost::container::flat_map<std::string, bool>>& map) |
| { |
| std::vector<std::string> ret; |
| // if any of the properties are true, return true |
| for (const auto& pair : map) |
| { |
| for (const auto& item : pair.second) |
| { |
| if (item.second) |
| { |
| ret.push_back(pair.first); |
| } |
| } |
| } |
| return ret; |
| } |
| |
| void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn, |
| bool forceRefresh = false) |
| { |
| std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap); |
| bool fatal = fatalVector.size(); |
| |
| std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap); |
| bool critical = criticalVector.size(); |
| |
| std::vector<std::string> warningVector = assertedInMap(warningAssertMap); |
| bool warn = warningVector.size(); |
| |
| associationManager->setLocalAssociations(fatalVector, criticalVector, |
| warningVector); |
| |
| StatusSetting last = currentPriority; |
| |
| if (fatal) |
| { |
| currentPriority = StatusSetting::fatal; |
| } |
| else if (critical) |
| { |
| currentPriority = StatusSetting::critical; |
| } |
| else if (warn) |
| { |
| currentPriority = StatusSetting::warn; |
| } |
| else |
| { |
| currentPriority = StatusSetting::ok; |
| } |
| |
| std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet; |
| |
| if (last != currentPriority || forceRefresh) |
| { |
| switch (currentPriority) |
| { |
| case (StatusSetting::fatal): |
| { |
| ledsToSet.push_back(std::make_pair(fatalLedPath, true)); |
| ledsToSet.push_back(std::make_pair(criticalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(warningLedPath, false)); |
| ledsToSet.push_back(std::make_pair(okLedPath, false)); |
| break; |
| } |
| case (StatusSetting::critical): |
| { |
| ledsToSet.push_back(std::make_pair(fatalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(criticalLedPath, true)); |
| ledsToSet.push_back(std::make_pair(warningLedPath, false)); |
| ledsToSet.push_back(std::make_pair(okLedPath, false)); |
| break; |
| } |
| case (StatusSetting::warn): |
| { |
| ledsToSet.push_back(std::make_pair(fatalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(criticalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(warningLedPath, true)); |
| ledsToSet.push_back(std::make_pair(okLedPath, false)); |
| break; |
| } |
| case (StatusSetting::ok): |
| { |
| ledsToSet.push_back(std::make_pair(fatalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(criticalLedPath, false)); |
| ledsToSet.push_back(std::make_pair(warningLedPath, false)); |
| ledsToSet.push_back(std::make_pair(okLedPath, true)); |
| break; |
| } |
| } |
| } |
| |
| for (const auto& ledPair : ledsToSet) |
| { |
| conn->async_method_call( |
| [ledPair](const boost::system::error_code ec) { |
| if (ec) |
| { |
| std::cerr << "Cannot set " << ledPair.first << " to " |
| << std::boolalpha |
| << std::get<bool>(ledPair.second) << "\n"; |
| } |
| if constexpr (debug) |
| { |
| std::cerr << "Set " << ledPair.first << " to " |
| << std::boolalpha |
| << std::get<bool>(ledPair.second) << "\n"; |
| } |
| }, |
| ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties", |
| "Set", ledIface, ledAssertProp, ledPair.second); |
| } |
| } |
| |
| void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) |
| { |
| |
| static sdbusplus::bus::match::match match( |
| static_cast<sdbusplus::bus::bus&>(*conn), |
| "type='signal',interface='org.freedesktop.DBus.Properties'," |
| "path_" |
| "namespace='/xyz/openbmc_project/" |
| "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", |
| [&conn](sdbusplus::message::message& message) { |
| std::string objectName; |
| boost::container::flat_map<std::string, std::variant<bool>> values; |
| |
| try |
| { |
| message.read(objectName, values); |
| } |
| catch (sdbusplus::exception_t&) |
| { |
| return; |
| } |
| if constexpr (debug) |
| { |
| std::cerr << "Threshold callback " << message.get_path() |
| << "\n"; |
| } |
| |
| auto findCriticalLow = values.find("CriticalAlarmLow"); |
| auto findCriticalHigh = values.find("CriticalAlarmHigh"); |
| |
| auto findWarnLow = values.find("WarningAlarmLow"); |
| auto findWarnHigh = values.find("WarningAlarmHigh"); |
| |
| if (findCriticalLow != values.end()) |
| { |
| criticalAssertMap[message.get_path()]["Low"] = |
| std::get<bool>(findCriticalLow->second); |
| } |
| if (findCriticalHigh != values.end()) |
| { |
| criticalAssertMap[message.get_path()]["High"] = |
| std::get<bool>(findCriticalHigh->second); |
| } |
| if (findWarnLow != values.end()) |
| { |
| warningAssertMap[message.get_path()]["Low"] = |
| std::get<bool>(findWarnLow->second); |
| } |
| if (findWarnHigh != values.end()) |
| { |
| warningAssertMap[message.get_path()]["High"] = |
| std::get<bool>(findWarnHigh->second); |
| } |
| |
| associationManager->setSensorAssociations( |
| assertedInMap(criticalAssertMap), |
| assertedInMap(warningAssertMap)); |
| |
| updateLedStatus(conn); |
| }); |
| } |
| |
| void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) |
| { |
| static sdbusplus::bus::match::match match( |
| static_cast<sdbusplus::bus::bus&>(*conn), |
| "type='signal',interface='org.freedesktop.DBus.Properties'," |
| "arg0namespace='" + |
| std::string(associationIface) + "'", |
| [&conn](sdbusplus::message::message& message) { |
| if (message.get_path() == rootPath) |
| { |
| return; // it's us |
| } |
| std::string objectName; |
| boost::container::flat_map<std::string, |
| std::variant<std::vector<Association>>> |
| values; |
| try |
| { |
| message.read(objectName, values); |
| } |
| catch (sdbusplus::exception_t&) |
| { |
| return; |
| } |
| |
| if constexpr (debug) |
| { |
| std::cerr << "Association callback " << message.get_path() |
| << "\n"; |
| } |
| |
| auto findAssociations = values.find("Associations"); |
| if (findAssociations == values.end()) |
| { |
| return; |
| } |
| const std::vector<Association>* associations = |
| std::get_if<std::vector<Association>>( |
| &findAssociations->second); |
| |
| if (associations == nullptr) |
| { |
| std::cerr << "Illegal Association on " << message.get_path() |
| << "\n"; |
| return; |
| } |
| |
| bool localWarning = false; |
| bool localCritical = false; |
| bool globalWarning = false; |
| bool globalCritical = false; |
| |
| for (const auto& [forward, reverse, path] : *associations) |
| { |
| if (path == rootPath) |
| { |
| globalWarning = globalWarning ? true : reverse == "warning"; |
| globalCritical = |
| globalCritical ? true : reverse == "critical"; |
| |
| if constexpr (1) |
| { |
| std::cerr << "got global "; |
| } |
| } |
| else |
| { |
| localWarning = localWarning ? true : reverse == "warning"; |
| localCritical = |
| localCritical ? true : reverse == "critical"; |
| } |
| if (globalCritical && localCritical) |
| { |
| break; |
| } |
| } |
| |
| bool fatal = globalCritical && localCritical; |
| bool critical = globalWarning && localCritical; |
| bool warning = globalWarning && !critical; |
| |
| fatalAssertMap[message.get_path()]["association"] = fatal; |
| criticalAssertMap[message.get_path()]["association"] = critical; |
| warningAssertMap[message.get_path()]["association"] = warning; |
| |
| updateLedStatus(conn); |
| }); |
| } |
| |
| int main(int argc, char** argv) |
| { |
| boost::asio::io_service io; |
| auto conn = std::make_shared<sdbusplus::asio::connection>(io); |
| conn->request_name("xyz.openbmc_project.CallbackManager"); |
| sdbusplus::asio::object_server objServer(conn); |
| std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface = |
| objServer.add_interface(rootPath, |
| "xyz.openbmc_project.CallbackManager"); |
| rootIface->register_method("RetriggerLEDUpdate", |
| [&conn]() { updateLedStatus(conn, true); }); |
| rootIface->initialize(); |
| |
| std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface = |
| objServer.add_interface(rootPath, globalInventoryIface); |
| inventoryIface->initialize(); |
| |
| associationManager = std::make_unique<AssociationManager>(objServer, conn); |
| |
| createThresholdMatch(conn); |
| createAssociationMatch(conn); |
| updateLedStatus(conn); |
| |
| io.run(); |
| |
| return 0; |
| } |