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 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 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 if (ec) 114 { 115 std::cerr << "Cannot set " << ledPair.first << " to " 116 << std::boolalpha 117 << std::get<bool>(ledPair.second) << "\n"; 118 } 119 if constexpr (debug) 120 { 121 std::cerr << "Set " << ledPair.first << " to " 122 << std::boolalpha 123 << std::get<bool>(ledPair.second) << "\n"; 124 } 125 }, 126 ledManagerBusname, ledPair.first, 127 "org.freedesktop.DBus.Properties", "Set", ledIface, 128 ledAssertProp, ledPair.second); 129 } 130 } 131 if (fatal) 132 { 133 currentPriority = StatusSetting::fatal; 134 } 135 else if (critical) 136 { 137 currentPriority = StatusSetting::critical; 138 } 139 else if (warn) 140 { 141 currentPriority = StatusSetting::warn; 142 } 143 else 144 { 145 currentPriority = StatusSetting::ok; 146 } 147 148 if (last != currentPriority || forceRefresh) 149 { 150 switch (currentPriority) 151 { 152 case (StatusSetting::fatal): 153 { 154 ledsToSet.push_back(std::make_pair(fatalLedPath, true)); 155 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 156 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 157 ledsToSet.push_back(std::make_pair(okLedPath, false)); 158 break; 159 } 160 case (StatusSetting::critical): 161 { 162 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 163 ledsToSet.push_back(std::make_pair(criticalLedPath, true)); 164 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 165 ledsToSet.push_back(std::make_pair(okLedPath, false)); 166 break; 167 } 168 case (StatusSetting::warn): 169 { 170 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 171 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 172 ledsToSet.push_back(std::make_pair(warningLedPath, true)); 173 ledsToSet.push_back(std::make_pair(okLedPath, false)); 174 break; 175 } 176 case (StatusSetting::ok): 177 { 178 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 179 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 180 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 181 ledsToSet.push_back(std::make_pair(okLedPath, true)); 182 break; 183 } 184 } 185 } 186 187 for (const auto& ledPair : ledsToSet) 188 { 189 conn->async_method_call( 190 [ledPair](const boost::system::error_code ec) { 191 if (ec) 192 { 193 std::cerr << "Cannot set " << ledPair.first << " to " 194 << std::boolalpha 195 << std::get<bool>(ledPair.second) << "\n"; 196 } 197 if constexpr (debug) 198 { 199 std::cerr << "Set " << ledPair.first << " to " 200 << std::boolalpha 201 << std::get<bool>(ledPair.second) << "\n"; 202 } 203 }, 204 ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties", 205 "Set", ledIface, ledAssertProp, ledPair.second); 206 } 207 } 208 209 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) 210 { 211 212 static sdbusplus::bus::match_t match( 213 static_cast<sdbusplus::bus_t&>(*conn), 214 "type='signal',member='ThresholdAsserted'", 215 [&conn](sdbusplus::message_t& message) { 216 std::string sensorName; 217 std::string thresholdInterface; 218 std::string event; 219 bool assert; 220 double assertValue; 221 222 try 223 { 224 message.read(sensorName, thresholdInterface, event, assert, 225 assertValue); 226 } 227 catch (sdbusplus::exception_t&) 228 { 229 return; 230 } 231 if constexpr (debug) 232 { 233 std::cerr << "Threshold callback: SensorName = " << sensorName 234 << ", Event = " << event << ", Asserted = " << assert 235 << "\n"; 236 } 237 238 if (event == "CriticalAlarmLow") 239 { 240 criticalAssertMap[message.get_path()]["low"] = assert; 241 } 242 else if (event == "CriticalAlarmHigh") 243 { 244 criticalAssertMap[message.get_path()]["high"] = assert; 245 } 246 else if (event == "WarningAlarmLow") 247 { 248 warningAssertMap[message.get_path()]["low"] = assert; 249 } 250 else if (event == "WarningAlarmHigh") 251 { 252 warningAssertMap[message.get_path()]["high"] = assert; 253 } 254 255 associationManager->setSensorAssociations( 256 assertedInMap(criticalAssertMap), 257 assertedInMap(warningAssertMap)); 258 259 updateLedStatus(conn); 260 }); 261 } 262 263 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) 264 { 265 static sdbusplus::bus::match_t match( 266 static_cast<sdbusplus::bus_t&>(*conn), 267 "type='signal',interface='org.freedesktop.DBus.Properties'," 268 "arg0namespace='" + 269 std::string(associationIface) + "'", 270 [&conn](sdbusplus::message_t& message) { 271 if (message.get_path() == rootPath) 272 { 273 return; // it's us 274 } 275 std::string objectName; 276 boost::container::flat_map<std::string, 277 std::variant<std::vector<Association>>> 278 values; 279 try 280 { 281 message.read(objectName, values); 282 } 283 catch (sdbusplus::exception_t&) 284 { 285 return; 286 } 287 288 if constexpr (debug) 289 { 290 std::cerr << "Association callback " << message.get_path() 291 << "\n"; 292 } 293 294 auto findAssociations = values.find("Associations"); 295 if (findAssociations == values.end()) 296 { 297 return; 298 } 299 const std::vector<Association>* associations = 300 std::get_if<std::vector<Association>>( 301 &findAssociations->second); 302 303 if (associations == nullptr) 304 { 305 std::cerr << "Illegal Association on " << message.get_path() 306 << "\n"; 307 return; 308 } 309 310 bool localWarning = false; 311 bool localCritical = false; 312 bool globalWarning = false; 313 bool globalCritical = false; 314 315 for (const auto& [forward, reverse, path] : *associations) 316 { 317 if (path == rootPath) 318 { 319 globalWarning = globalWarning ? true : reverse == "warning"; 320 globalCritical = 321 globalCritical ? true : reverse == "critical"; 322 323 if constexpr (1) 324 { 325 std::cerr << "got global "; 326 } 327 } 328 else 329 { 330 localWarning = localWarning ? true : reverse == "warning"; 331 localCritical = 332 localCritical ? true : reverse == "critical"; 333 } 334 if (globalCritical && localCritical) 335 { 336 break; 337 } 338 } 339 340 bool fatal = globalCritical && localCritical; 341 bool critical = globalWarning && localCritical; 342 bool warning = globalWarning && !critical; 343 344 fatalAssertMap[message.get_path()]["association"] = fatal; 345 criticalAssertMap[message.get_path()]["association"] = critical; 346 warningAssertMap[message.get_path()]["association"] = warning; 347 348 updateLedStatus(conn); 349 }); 350 } 351 352 int main(int argc, char** argv) 353 { 354 boost::asio::io_context io; 355 auto conn = std::make_shared<sdbusplus::asio::connection>(io); 356 conn->request_name("xyz.openbmc_project.CallbackManager"); 357 sdbusplus::asio::object_server objServer(conn); 358 std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface = 359 objServer.add_interface(rootPath, 360 "xyz.openbmc_project.CallbackManager"); 361 rootIface->register_method("RetriggerLEDUpdate", 362 [&conn]() { updateLedStatus(conn, true); }); 363 rootIface->initialize(); 364 365 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface = 366 objServer.add_interface(rootPath, globalInventoryIface); 367 inventoryIface->initialize(); 368 369 associationManager = std::make_unique<AssociationManager>(objServer, conn); 370 371 createThresholdMatch(conn); 372 createAssociationMatch(conn); 373 updateLedStatus(conn); 374 375 io.run(); 376 377 return 0; 378 } 379