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 <PSUEvent.hpp> 18 #include <SensorPaths.hpp> 19 #include <boost/asio/io_service.hpp> 20 #include <boost/asio/read_until.hpp> 21 #include <boost/container/flat_map.hpp> 22 #include <phosphor-logging/lg2.hpp> 23 #include <sdbusplus/asio/connection.hpp> 24 #include <sdbusplus/asio/object_server.hpp> 25 26 #include <iostream> 27 #include <memory> 28 #include <stdexcept> 29 #include <string> 30 #include <utility> 31 #include <variant> 32 #include <vector> 33 34 PSUCombineEvent::PSUCombineEvent( 35 sdbusplus::asio::object_server& objectServer, 36 std::shared_ptr<sdbusplus::asio::connection>& conn, 37 boost::asio::io_service& io, const std::string& psuName, 38 const PowerState& powerState, 39 boost::container::flat_map<std::string, std::vector<std::string>>& 40 eventPathList, 41 boost::container::flat_map< 42 std::string, 43 boost::container::flat_map<std::string, std::vector<std::string>>>& 44 groupEventPathList, 45 const std::string& combineEventName, double pollRate) : 46 objServer(objectServer) 47 { 48 std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName); 49 eventInterface = objServer.add_interface( 50 "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" + 51 combineEventName, 52 "xyz.openbmc_project.State.Decorator.OperationalStatus"); 53 eventInterface->register_property("functional", true); 54 55 if (!eventInterface->initialize()) 56 { 57 std::cerr << "error initializing event interface\n"; 58 } 59 60 std::shared_ptr<std::set<std::string>> combineEvent = 61 std::make_shared<std::set<std::string>>(); 62 for (const auto& pathList : eventPathList) 63 { 64 std::shared_ptr<std::set<std::string>> assert = 65 std::make_shared<std::set<std::string>>(); 66 std::shared_ptr<bool> state = std::make_shared<bool>(false); 67 68 const std::string& eventName = pathList.first; 69 std::string eventPSUName = eventName + psuName; 70 for (const auto& path : pathList.second) 71 { 72 auto p = std::make_shared<PSUSubEvent>( 73 eventInterface, path, conn, io, powerState, eventName, 74 eventName, assert, combineEvent, state, psuName, pollRate); 75 p->setupRead(); 76 77 events[eventPSUName].emplace_back(p); 78 asserts.emplace_back(assert); 79 states.emplace_back(state); 80 } 81 } 82 83 for (const auto& groupPathList : groupEventPathList) 84 { 85 for (const auto& pathList : groupPathList.second) 86 { 87 std::shared_ptr<std::set<std::string>> assert = 88 std::make_shared<std::set<std::string>>(); 89 std::shared_ptr<bool> state = std::make_shared<bool>(false); 90 91 const std::string& groupEventName = pathList.first; 92 std::string eventPSUName = groupEventName + psuName; 93 for (const auto& path : pathList.second) 94 { 95 auto p = std::make_shared<PSUSubEvent>( 96 eventInterface, path, conn, io, powerState, groupEventName, 97 groupPathList.first, assert, combineEvent, state, psuName, 98 pollRate); 99 p->setupRead(); 100 events[eventPSUName].emplace_back(p); 101 102 asserts.emplace_back(assert); 103 states.emplace_back(state); 104 } 105 } 106 } 107 } 108 109 PSUCombineEvent::~PSUCombineEvent() 110 { 111 // Clear unique_ptr first 112 for (auto& event : events) 113 { 114 for (auto& subEventPtr : event.second) 115 { 116 subEventPtr.reset(); 117 } 118 } 119 events.clear(); 120 objServer.remove_interface(eventInterface); 121 } 122 123 static boost::container::flat_map<std::string, 124 std::pair<std::string, std::string>> 125 logID = { 126 {"PredictiveFailure", 127 {"OpenBMC.0.1.PowerSupplyFailurePredicted", 128 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}}, 129 {"Failure", 130 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}}, 131 {"ACLost", 132 {"OpenBMC.0.1.PowerSupplyPowerLost", 133 "OpenBMC.0.1.PowerSupplyPowerRestored"}}, 134 {"FanFault", 135 {"OpenBMC.0.1.PowerSupplyFanFailed", 136 "OpenBMC.0.1.PowerSupplyFanRecovered"}}, 137 {"ConfigureError", 138 {"OpenBMC.0.1.PowerSupplyConfigurationError", 139 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}}; 140 141 PSUSubEvent::PSUSubEvent( 142 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface, 143 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn, 144 boost::asio::io_service& io, const PowerState& powerState, 145 const std::string& groupEventName, const std::string& eventName, 146 std::shared_ptr<std::set<std::string>> asserts, 147 std::shared_ptr<std::set<std::string>> combineEvent, 148 std::shared_ptr<bool> state, const std::string& psuName, double pollRate) : 149 std::enable_shared_from_this<PSUSubEvent>(), 150 eventInterface(std::move(eventInterface)), asserts(std::move(asserts)), 151 combineEvent(std::move(combineEvent)), assertState(std::move(state)), 152 errCount(0), path(path), eventName(eventName), readState(powerState), 153 waitTimer(io), inputDev(io), psuName(psuName), 154 groupEventName(groupEventName), systemBus(conn) 155 { 156 if (pollRate > 0.0) 157 { 158 eventPollMs = static_cast<unsigned int>(pollRate * 1000); 159 } 160 161 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 162 fd = open(path.c_str(), O_RDONLY); 163 if (fd < 0) 164 { 165 std::cerr << "PSU sub event failed to open file\n"; 166 return; 167 } 168 inputDev.assign(fd); 169 170 auto found = logID.find(eventName); 171 if (found == logID.end()) 172 { 173 assertMessage.clear(); 174 deassertMessage.clear(); 175 } 176 else 177 { 178 assertMessage = found->second.first; 179 deassertMessage = found->second.second; 180 } 181 182 auto fanPos = path.find("fan"); 183 if (fanPos != std::string::npos) 184 { 185 fanName = path.substr(fanPos); 186 auto fanNamePos = fanName.find('_'); 187 if (fanNamePos != std::string::npos) 188 { 189 fanName = fanName.substr(0, fanNamePos); 190 } 191 } 192 } 193 194 PSUSubEvent::~PSUSubEvent() 195 { 196 waitTimer.cancel(); 197 inputDev.close(); 198 } 199 200 void PSUSubEvent::setupRead(void) 201 { 202 if (!readingStateGood(readState)) 203 { 204 // Deassert the event 205 updateValue(0); 206 restartRead(); 207 return; 208 } 209 210 std::shared_ptr<boost::asio::streambuf> buffer = 211 std::make_shared<boost::asio::streambuf>(); 212 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this(); 213 boost::asio::async_read_until( 214 inputDev, *buffer, '\n', 215 [weakRef, buffer](const boost::system::error_code& ec, 216 std::size_t /*bytes_transfered*/) { 217 std::shared_ptr<PSUSubEvent> self = weakRef.lock(); 218 if (self) 219 { 220 self->readBuf = buffer; 221 self->handleResponse(ec); 222 } 223 }); 224 } 225 226 void PSUSubEvent::restartRead() 227 { 228 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this(); 229 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs)); 230 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) { 231 if (ec == boost::asio::error::operation_aborted) 232 { 233 return; 234 } 235 std::shared_ptr<PSUSubEvent> self = weakRef.lock(); 236 if (self) 237 { 238 self->setupRead(); 239 } 240 }); 241 } 242 243 void PSUSubEvent::handleResponse(const boost::system::error_code& err) 244 { 245 if ((err == boost::system::errc::bad_file_descriptor) || 246 (err == boost::asio::error::misc_errors::not_found)) 247 { 248 return; 249 } 250 std::istream responseStream(readBuf.get()); 251 if (!err) 252 { 253 std::string response; 254 try 255 { 256 std::getline(responseStream, response); 257 int nvalue = std::stoi(response); 258 responseStream.clear(); 259 260 updateValue(nvalue); 261 errCount = 0; 262 } 263 catch (const std::invalid_argument&) 264 { 265 errCount++; 266 } 267 } 268 else 269 { 270 errCount++; 271 } 272 if (errCount >= warnAfterErrorCount) 273 { 274 if (errCount == warnAfterErrorCount) 275 { 276 std::cerr << "Failure to read event at " << path << "\n"; 277 } 278 updateValue(0); 279 errCount++; 280 } 281 lseek(fd, 0, SEEK_SET); 282 restartRead(); 283 } 284 285 // Any of the sub events of one event is asserted, then the event will be 286 // asserted. Only if none of the sub events are asserted, the event will be 287 // deasserted. 288 void PSUSubEvent::updateValue(const int& newValue) 289 { 290 // Take no action if value already equal 291 // Same semantics as Sensor::updateValue(const double&) 292 if (newValue == value) 293 { 294 return; 295 } 296 297 if (newValue == 0) 298 { 299 // log deassert only after all asserts are gone 300 if (!(*asserts).empty()) 301 { 302 auto found = (*asserts).find(path); 303 if (found == (*asserts).end()) 304 { 305 return; 306 } 307 (*asserts).erase(path); 308 309 return; 310 } 311 if (*assertState == true) 312 { 313 *assertState = false; 314 auto foundCombine = (*combineEvent).find(groupEventName); 315 if (foundCombine == (*combineEvent).end()) 316 { 317 return; 318 } 319 (*combineEvent).erase(groupEventName); 320 if (!deassertMessage.empty()) 321 { 322 // Fan Failed has two args 323 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered") 324 { 325 lg2::info("{EVENT} deassert", "EVENT", eventName, 326 "REDFISH_MESSAGE_ID", deassertMessage, 327 "REDFISH_MESSAGE_ARGS", 328 (psuName + ',' + fanName)); 329 } 330 else 331 { 332 lg2::info("{EVENT} deassert", "EVENT", eventName, 333 "REDFISH_MESSAGE_ID", deassertMessage, 334 "REDFISH_MESSAGE_ARGS", psuName); 335 } 336 } 337 338 if ((*combineEvent).empty()) 339 { 340 eventInterface->set_property("functional", true); 341 } 342 } 343 } 344 else 345 { 346 std::cerr << "PSUSubEvent asserted by " << path << "\n"; 347 348 if ((*assertState == false) && ((*asserts).empty())) 349 { 350 *assertState = true; 351 if (!assertMessage.empty()) 352 { 353 // Fan Failed has two args 354 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed") 355 { 356 lg2::warning("{EVENT} assert", "EVENT", eventName, 357 "REDFISH_MESSAGE_ID", assertMessage, 358 "REDFISH_MESSAGE_ARGS", 359 (psuName + ',' + fanName)); 360 } 361 else 362 { 363 lg2::warning("{EVENT} assert", "EVENT", eventName, 364 "REDFISH_MESSAGE_ID", assertMessage, 365 "REDFISH_MESSAGE_ARGS", psuName); 366 } 367 } 368 if ((*combineEvent).empty()) 369 { 370 eventInterface->set_property("functional", false); 371 } 372 (*combineEvent).emplace(groupEventName); 373 } 374 (*asserts).emplace(path); 375 } 376 value = newValue; 377 } 378