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