/* // 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 #include #include #include #include #include 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; enum class StatusSetting { none, ok, warn, critical, fatal }; constexpr const bool debug = false; // final led state tracking StatusSetting currentPriority = StatusSetting::none; // maps of > boost::container::flat_map> fatalAssertMap; boost::container::flat_map> criticalAssertMap; boost::container::flat_map> warningAssertMap; std::vector assertedInMap( const boost::container::flat_map< std::string, boost::container::flat_map>& map) { std::vector 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& conn, bool forceRefresh = false) { std::vector fatalVector = assertedInMap(fatalAssertMap); bool fatal = fatalVector.size(); std::vector criticalVector = assertedInMap(criticalAssertMap); bool critical = criticalVector.size(); std::vector warningVector = assertedInMap(warningAssertMap); bool warn = warningVector.size(); associationManager->setLocalAssociations(fatalVector, criticalVector, warningVector); StatusSetting last = currentPriority; std::vector>> ledsToSet; if (forceRefresh) { 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, false)); for (const auto& ledPair : ledsToSet) { conn->async_method_call( [ledPair](const boost::system::error_code ec) { std::ios_base::fmtflags originalFlags = std::cerr.flags(); if (ec) { std::cerr << "Cannot set " << ledPair.first << " to " << std::boolalpha << std::get(ledPair.second) << "\n"; std::cerr.flags(originalFlags); } if constexpr (debug) { std::cerr << "Set " << ledPair.first << " to " << std::boolalpha << std::get(ledPair.second) << "\n"; std::cerr.flags(originalFlags); } }, ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties", "Set", ledIface, ledAssertProp, ledPair.second); } } if (fatal) { currentPriority = StatusSetting::fatal; } else if (critical) { currentPriority = StatusSetting::critical; } else if (warn) { currentPriority = StatusSetting::warn; } else { currentPriority = StatusSetting::ok; } 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(ledPair.second) << "\n"; } if constexpr (debug) { std::cerr << "Set " << ledPair.first << " to " << std::boolalpha << std::get(ledPair.second) << "\n"; } }, ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties", "Set", ledIface, ledAssertProp, ledPair.second); } } void createThresholdMatch(std::shared_ptr& conn) { static sdbusplus::bus::match_t match( static_cast(*conn), "type='signal',member='ThresholdAsserted'", [&conn](sdbusplus::message_t& message) { std::string sensorName; std::string thresholdInterface; std::string event; bool assert; double assertValue; try { message.read(sensorName, thresholdInterface, event, assert, assertValue); } catch (sdbusplus::exception_t&) { return; } if constexpr (debug) { std::cerr << "Threshold callback: SensorName = " << sensorName << ", Event = " << event << ", Asserted = " << assert << "\n"; } if (event == "CriticalAlarmLow") { criticalAssertMap[message.get_path()]["low"] = assert; } else if (event == "CriticalAlarmHigh") { criticalAssertMap[message.get_path()]["high"] = assert; } else if (event == "WarningAlarmLow") { warningAssertMap[message.get_path()]["low"] = assert; } else if (event == "WarningAlarmHigh") { warningAssertMap[message.get_path()]["high"] = assert; } associationManager->setSensorAssociations( assertedInMap(criticalAssertMap), assertedInMap(warningAssertMap)); updateLedStatus(conn); }); } void createAssociationMatch(std::shared_ptr& conn) { static sdbusplus::bus::match_t match( static_cast(*conn), "type='signal',interface='org.freedesktop.DBus.Properties'," "arg0namespace='" + std::string(associationIface) + "'", [&conn](sdbusplus::message_t& message) { if (message.get_path() == rootPath) { return; // it's us } std::string objectName; boost::container::flat_map>> 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* associations = std::get_if>( &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_context io; auto conn = std::make_shared(io); conn->request_name("xyz.openbmc_project.CallbackManager"); sdbusplus::asio::object_server objServer(conn); std::shared_ptr rootIface = objServer.add_interface(rootPath, "xyz.openbmc_project.CallbackManager"); rootIface->register_method("RetriggerLEDUpdate", [&conn]() { updateLedStatus(conn, true); }); rootIface->initialize(); std::shared_ptr inventoryIface = objServer.add_interface(rootPath, globalInventoryIface); inventoryIface->initialize(); associationManager = std::make_unique(objServer, conn); createThresholdMatch(conn); createAssociationMatch(conn); updateLedStatus(conn); io.run(); return 0; }