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