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