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/container/flat_map.hpp> 20 #include <sdbusplus/asio/connection.hpp> 21 #include <sdbusplus/asio/object_server.hpp> 22 #include <variant> 23 24 constexpr const char* fatalLedPath = 25 "/xyz/openbmc_project/led/groups/status_critical"; 26 constexpr const char* criticalLedPath = 27 "/xyz/openbmc_project/led/groups/status_non_critical"; 28 constexpr const char* warningLedPath = 29 "/xyz/openbmc_project/led/groups/status_degraded"; 30 constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok"; 31 32 constexpr const char* ledIface = "xyz.openbmc_project.Led.Group"; 33 constexpr const char* ledAssertProp = "Asserted"; 34 constexpr const char* ledManagerBusname = 35 "xyz.openbmc_project.LED.GroupManager"; 36 37 std::unique_ptr<AssociationManager> associationManager; 38 39 enum class StatusSetting 40 { 41 none, 42 ok, 43 warn, 44 critical, 45 fatal 46 }; 47 48 constexpr const bool debug = false; 49 50 // final led state tracking 51 StatusSetting currentPriority = StatusSetting::none; 52 53 // maps of <object-path, <property, asserted>> 54 boost::container::flat_map<std::string, 55 boost::container::flat_map<std::string, bool>> 56 fatalAssertMap; 57 boost::container::flat_map<std::string, 58 boost::container::flat_map<std::string, bool>> 59 criticalAssertMap; 60 boost::container::flat_map<std::string, 61 boost::container::flat_map<std::string, bool>> 62 warningAssertMap; 63 64 std::vector<std::string> assertedInMap( 65 const boost::container::flat_map< 66 std::string, boost::container::flat_map<std::string, bool>>& map) 67 { 68 std::vector<std::string> ret; 69 // if any of the properties are true, return true 70 for (const auto& pair : map) 71 { 72 for (const auto& item : pair.second) 73 { 74 if (item.second) 75 { 76 ret.push_back(pair.first); 77 } 78 } 79 } 80 return ret; 81 } 82 83 void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn, 84 bool forceRefresh = false) 85 { 86 std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap); 87 bool fatal = fatalVector.size(); 88 89 std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap); 90 bool critical = criticalVector.size(); 91 92 std::vector<std::string> warningVector = assertedInMap(warningAssertMap); 93 bool warn = warningVector.size(); 94 95 associationManager->setLocalAssociations(fatalVector, criticalVector, 96 warningVector); 97 98 StatusSetting last = currentPriority; 99 100 if (fatal) 101 { 102 currentPriority = StatusSetting::fatal; 103 } 104 else if (critical) 105 { 106 currentPriority = StatusSetting::critical; 107 } 108 else if (warn) 109 { 110 currentPriority = StatusSetting::warn; 111 } 112 else 113 { 114 currentPriority = StatusSetting::ok; 115 } 116 117 std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet; 118 119 if (last != currentPriority || forceRefresh) 120 { 121 switch (currentPriority) 122 { 123 case (StatusSetting::fatal): 124 { 125 ledsToSet.push_back(std::make_pair(fatalLedPath, true)); 126 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 127 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 128 ledsToSet.push_back(std::make_pair(okLedPath, false)); 129 break; 130 } 131 case (StatusSetting::critical): 132 { 133 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 134 ledsToSet.push_back(std::make_pair(criticalLedPath, true)); 135 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 136 ledsToSet.push_back(std::make_pair(okLedPath, false)); 137 break; 138 } 139 case (StatusSetting::warn): 140 { 141 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 142 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 143 ledsToSet.push_back(std::make_pair(warningLedPath, true)); 144 ledsToSet.push_back(std::make_pair(okLedPath, false)); 145 break; 146 } 147 case (StatusSetting::ok): 148 { 149 ledsToSet.push_back(std::make_pair(fatalLedPath, false)); 150 ledsToSet.push_back(std::make_pair(criticalLedPath, false)); 151 ledsToSet.push_back(std::make_pair(warningLedPath, false)); 152 ledsToSet.push_back(std::make_pair(okLedPath, true)); 153 break; 154 } 155 } 156 } 157 158 for (const auto& ledPair : ledsToSet) 159 { 160 conn->async_method_call( 161 [ledPair](const boost::system::error_code ec) { 162 if (ec) 163 { 164 std::cerr << "Cannot set " << ledPair.first << " to " 165 << std::boolalpha 166 << std::get<bool>(ledPair.second) << "\n"; 167 } 168 if constexpr (debug) 169 { 170 std::cerr << "Set " << ledPair.first << " to " 171 << std::boolalpha 172 << std::get<bool>(ledPair.second) << "\n"; 173 } 174 }, 175 ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties", 176 "Set", ledIface, ledAssertProp, ledPair.second); 177 } 178 } 179 180 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) 181 { 182 183 static sdbusplus::bus::match::match match( 184 static_cast<sdbusplus::bus::bus&>(*conn), 185 "type='signal',interface='org.freedesktop.DBus.Properties'," 186 "path_" 187 "namespace='/xyz/openbmc_project/" 188 "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", 189 [&conn](sdbusplus::message::message& message) { 190 std::string objectName; 191 boost::container::flat_map<std::string, std::variant<bool>> values; 192 193 try 194 { 195 message.read(objectName, values); 196 } 197 catch (sdbusplus::exception_t&) 198 { 199 return; 200 } 201 if constexpr (debug) 202 { 203 std::cerr << "Threshold callback " << message.get_path() 204 << "\n"; 205 } 206 207 auto findCriticalLow = values.find("CriticalAlarmLow"); 208 auto findCriticalHigh = values.find("CriticalAlarmHigh"); 209 210 auto findWarnLow = values.find("WarningAlarmLow"); 211 auto findWarnHigh = values.find("WarningAlarmHigh"); 212 213 if (findCriticalLow != values.end()) 214 { 215 criticalAssertMap[message.get_path()]["Low"] = 216 std::get<bool>(findCriticalLow->second); 217 } 218 if (findCriticalHigh != values.end()) 219 { 220 criticalAssertMap[message.get_path()]["High"] = 221 std::get<bool>(findCriticalHigh->second); 222 } 223 if (findWarnLow != values.end()) 224 { 225 warningAssertMap[message.get_path()]["Low"] = 226 std::get<bool>(findWarnLow->second); 227 } 228 if (findWarnHigh != values.end()) 229 { 230 warningAssertMap[message.get_path()]["High"] = 231 std::get<bool>(findWarnHigh->second); 232 } 233 234 associationManager->setSensorAssociations( 235 assertedInMap(criticalAssertMap), 236 assertedInMap(warningAssertMap)); 237 238 updateLedStatus(conn); 239 }); 240 } 241 242 void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn) 243 { 244 static sdbusplus::bus::match::match match( 245 static_cast<sdbusplus::bus::bus&>(*conn), 246 "type='signal',interface='org.freedesktop.DBus.Properties'," 247 "arg0namespace='" + 248 std::string(associationIface) + "'", 249 [&conn](sdbusplus::message::message& message) { 250 if (message.get_path() == rootPath) 251 { 252 return; // it's us 253 } 254 std::string objectName; 255 boost::container::flat_map<std::string, 256 std::variant<std::vector<Association>>> 257 values; 258 try 259 { 260 message.read(objectName, values); 261 } 262 catch (sdbusplus::exception_t&) 263 { 264 return; 265 } 266 267 if constexpr (debug) 268 { 269 std::cerr << "Association callback " << message.get_path() 270 << "\n"; 271 } 272 273 auto findAssociations = values.find("associations"); 274 if (findAssociations == values.end()) 275 { 276 return; 277 } 278 const std::vector<Association>* associations = 279 std::get_if<std::vector<Association>>( 280 &findAssociations->second); 281 282 if (associations == nullptr) 283 { 284 std::cerr << "Illegal Association on " << message.get_path() 285 << "\n"; 286 return; 287 } 288 289 bool localWarning = false; 290 bool localCritical = false; 291 bool globalWarning = false; 292 bool globalCritical = false; 293 294 for (const auto& [forward, reverse, path] : *associations) 295 { 296 if (path == rootPath) 297 { 298 globalWarning = globalWarning ? true : reverse == "warning"; 299 globalCritical = 300 globalCritical ? true : reverse == "critical"; 301 302 if constexpr (1) 303 { 304 std::cerr << "got global "; 305 } 306 } 307 else 308 { 309 localWarning = localWarning ? true : reverse == "warning"; 310 localCritical = 311 localCritical ? true : reverse == "critical"; 312 } 313 if (globalCritical && localCritical) 314 { 315 break; 316 } 317 } 318 319 bool fatal = globalCritical && localCritical; 320 bool critical = globalWarning && localCritical; 321 bool warning = globalWarning && localWarning; 322 323 fatalAssertMap[message.get_path()]["association"] = fatal; 324 criticalAssertMap[message.get_path()]["association"] = critical; 325 warningAssertMap[message.get_path()]["association"] = warning; 326 327 updateLedStatus(conn); 328 }); 329 } 330 331 int main(int argc, char** argv) 332 { 333 boost::asio::io_service io; 334 auto conn = std::make_shared<sdbusplus::asio::connection>(io); 335 conn->request_name("xyz.openbmc_project.CallbackManager"); 336 sdbusplus::asio::object_server objServer(conn); 337 std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface = 338 objServer.add_interface(rootPath, 339 "xyz.openbmc_project.CallbackManager"); 340 rootIface->register_method("RetriggerLEDUpdate", 341 [&conn]() { updateLedStatus(conn, true); }); 342 rootIface->initialize(); 343 344 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface = 345 objServer.add_interface(rootPath, globalInventoryIface); 346 inventoryIface->initialize(); 347 348 associationManager = std::make_unique<AssociationManager>(objServer, conn); 349 350 createThresholdMatch(conn); 351 createAssociationMatch(conn); 352 updateLedStatus(conn); 353 354 io.run(); 355 356 return 0; 357 } 358