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