1711d51d8SMatt Spinler /** 2711d51d8SMatt Spinler * Copyright © 2019 IBM Corporation 3711d51d8SMatt Spinler * 4711d51d8SMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5711d51d8SMatt Spinler * you may not use this file except in compliance with the License. 6711d51d8SMatt Spinler * You may obtain a copy of the License at 7711d51d8SMatt Spinler * 8711d51d8SMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9711d51d8SMatt Spinler * 10711d51d8SMatt Spinler * Unless required by applicable law or agreed to in writing, software 11711d51d8SMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12711d51d8SMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13711d51d8SMatt Spinler * See the License for the specific language governing permissions and 14711d51d8SMatt Spinler * limitations under the License. 15711d51d8SMatt Spinler */ 1689fa082aSMatt Spinler #include "repository.hpp" 1789fa082aSMatt Spinler 1832a6df6cSMatt Spinler #include <fcntl.h> 19dd325c32SMatt Spinler #include <sys/stat.h> 20dd325c32SMatt Spinler 21db3f2798SMatt Spinler #include <phosphor-logging/lg2.hpp> 2289fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2389fa082aSMatt Spinler 242544b419SPatrick Williams #include <fstream> 252544b419SPatrick Williams 2689fa082aSMatt Spinler namespace openpower 2789fa082aSMatt Spinler { 2889fa082aSMatt Spinler namespace pels 2989fa082aSMatt Spinler { 3089fa082aSMatt Spinler 3189fa082aSMatt Spinler namespace fs = std::filesystem; 3289fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3389fa082aSMatt Spinler 347e727a39SMatt Spinler constexpr size_t warningPercentage = 95; 357e727a39SMatt Spinler 36dd325c32SMatt Spinler /** 37dd325c32SMatt Spinler * @brief Returns the amount of space the file uses on disk. 38dd325c32SMatt Spinler * 39dd325c32SMatt Spinler * This is different than just the regular size of the file. 40dd325c32SMatt Spinler * 41dd325c32SMatt Spinler * @param[in] file - The file to get the size of 42dd325c32SMatt Spinler * 43dd325c32SMatt Spinler * @return size_t The disk space the file uses 44dd325c32SMatt Spinler */ 45dd325c32SMatt Spinler size_t getFileDiskSize(const std::filesystem::path& file) 46dd325c32SMatt Spinler { 47dd325c32SMatt Spinler constexpr size_t statBlockSize = 512; 48dd325c32SMatt Spinler struct stat statData; 49dd325c32SMatt Spinler auto rc = stat(file.c_str(), &statData); 50dd325c32SMatt Spinler if (rc != 0) 51dd325c32SMatt Spinler { 52dd325c32SMatt Spinler auto e = errno; 53db3f2798SMatt Spinler lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE", 54db3f2798SMatt Spinler file.native(), "ERRNO", e); 55dd325c32SMatt Spinler abort(); 56dd325c32SMatt Spinler } 57dd325c32SMatt Spinler 58dd325c32SMatt Spinler return statData.st_blocks * statBlockSize; 59dd325c32SMatt Spinler } 60dd325c32SMatt Spinler 618d5f3a2bSMatt Spinler Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 628d5f3a2bSMatt Spinler size_t maxNumPELs) : 638d5f3a2bSMatt Spinler _logPath(basePath / "logs"), 641d8835bbSSumit Kumar _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs), 651d8835bbSSumit Kumar _archivePath(basePath / "logs" / "archive") 6689fa082aSMatt Spinler { 6789fa082aSMatt Spinler if (!fs::exists(_logPath)) 6889fa082aSMatt Spinler { 6989fa082aSMatt Spinler fs::create_directories(_logPath); 7089fa082aSMatt Spinler } 71475e574dSMatt Spinler 721d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 731d8835bbSSumit Kumar { 741d8835bbSSumit Kumar fs::create_directories(_archivePath); 751d8835bbSSumit Kumar } 761d8835bbSSumit Kumar 77475e574dSMatt Spinler restore(); 78475e574dSMatt Spinler } 79475e574dSMatt Spinler 80475e574dSMatt Spinler void Repository::restore() 81475e574dSMatt Spinler { 82475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 83475e574dSMatt Spinler { 84475e574dSMatt Spinler try 85475e574dSMatt Spinler { 86475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 87475e574dSMatt Spinler { 88475e574dSMatt Spinler continue; 89475e574dSMatt Spinler } 90475e574dSMatt Spinler 91475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 92475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 93475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 94475e574dSMatt Spinler file.close(); 95475e574dSMatt Spinler 9607eefc54SMatt Spinler PEL pel{data}; 97475e574dSMatt Spinler if (pel.valid()) 98475e574dSMatt Spinler { 99a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 100a3c12a48SMatt Spinler // it will get sent up again. 101a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 102a3c12a48SMatt Spinler { 103a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 104a3c12a48SMatt Spinler try 105a3c12a48SMatt Spinler { 106a3c12a48SMatt Spinler write(pel, dirEntry.path()); 107a3c12a48SMatt Spinler } 10866491c61SPatrick Williams catch (const std::exception& e) 109a3c12a48SMatt Spinler { 110db3f2798SMatt Spinler lg2::error( 111db3f2798SMatt Spinler "Failed to save PEL after updating host state, PEL ID = {ID}", 112db3f2798SMatt Spinler "ID", lg2::hex, pel.id()); 113a3c12a48SMatt Spinler } 114a3c12a48SMatt Spinler } 115a3c12a48SMatt Spinler 1168e65f4eaSMatt Spinler PELAttributes attributes{ 1178e65f4eaSMatt Spinler dirEntry.path(), 118dd325c32SMatt Spinler getFileDiskSize(dirEntry.path()), 119dd325c32SMatt Spinler pel.privateHeader().creatorID(), 120afb1b46fSVijay Lobo pel.userHeader().subsystem(), 121dd325c32SMatt Spinler pel.userHeader().severity(), 122dd325c32SMatt Spinler pel.userHeader().actionFlags(), 123dd325c32SMatt Spinler pel.hostTransmissionState(), 1248e65f4eaSMatt Spinler pel.hmcTransmissionState(), 1258e65f4eaSMatt Spinler pel.plid(), 1268e65f4eaSMatt Spinler pel.getDeconfigFlag(), 1278e65f4eaSMatt Spinler pel.getGuardFlag(), 1288e65f4eaSMatt Spinler getMillisecondsSinceEpoch( 1298e65f4eaSMatt Spinler pel.privateHeader().createTimestamp())}; 1300ff00485SMatt Spinler 131475e574dSMatt Spinler using pelID = LogID::Pel; 132475e574dSMatt Spinler using obmcID = LogID::Obmc; 1330ff00485SMatt Spinler _pelAttributes.emplace( 134475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 1350ff00485SMatt Spinler attributes); 136b188f78aSMatt Spinler 137b188f78aSMatt Spinler updateRepoStats(attributes, true); 138475e574dSMatt Spinler } 139475e574dSMatt Spinler else 140475e574dSMatt Spinler { 141db3f2798SMatt Spinler lg2::error( 142db3f2798SMatt Spinler "Found invalid PEL file {FILE} while restoring. Removing.", 143db3f2798SMatt Spinler "FILE", dirEntry.path()); 144475e574dSMatt Spinler fs::remove(dirEntry.path()); 145475e574dSMatt Spinler } 146475e574dSMatt Spinler } 14766491c61SPatrick Williams catch (const std::exception& e) 148475e574dSMatt Spinler { 149db3f2798SMatt Spinler lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}", 150db3f2798SMatt Spinler "FILE", dirEntry.path(), "ERROR", e); 151475e574dSMatt Spinler } 152475e574dSMatt Spinler } 1531d8835bbSSumit Kumar 1541d8835bbSSumit Kumar // Get size of archive folder 1551d8835bbSSumit Kumar for (auto& dirEntry : fs::directory_iterator(_archivePath)) 1561d8835bbSSumit Kumar { 1571d8835bbSSumit Kumar _archiveSize += getFileDiskSize(dirEntry); 1581d8835bbSSumit Kumar } 15989fa082aSMatt Spinler } 16089fa082aSMatt Spinler 16189fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 16289fa082aSMatt Spinler { 16389fa082aSMatt Spinler char name[50]; 16489fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 16589fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 16689fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 16789fa082aSMatt Spinler return std::string{name}; 16889fa082aSMatt Spinler } 16989fa082aSMatt Spinler 17089fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 17189fa082aSMatt Spinler { 172df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 173df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 174df43a305SMatt Spinler 17589fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 176ab1b97feSMatt Spinler 177ab1b97feSMatt Spinler write(*(pel.get()), path); 178ab1b97feSMatt Spinler 1798e65f4eaSMatt Spinler PELAttributes attributes{ 1808e65f4eaSMatt Spinler path, 181dd325c32SMatt Spinler getFileDiskSize(path), 182dd325c32SMatt Spinler pel->privateHeader().creatorID(), 183afb1b46fSVijay Lobo pel->userHeader().subsystem(), 184dd325c32SMatt Spinler pel->userHeader().severity(), 185dd325c32SMatt Spinler pel->userHeader().actionFlags(), 186346f99a1SMatt Spinler pel->hostTransmissionState(), 1878e65f4eaSMatt Spinler pel->hmcTransmissionState(), 1888e65f4eaSMatt Spinler pel->plid(), 1898e65f4eaSMatt Spinler pel->getDeconfigFlag(), 1908e65f4eaSMatt Spinler pel->getGuardFlag(), 1918e65f4eaSMatt Spinler getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())}; 192ab1b97feSMatt Spinler 193ab1b97feSMatt Spinler using pelID = LogID::Pel; 194ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 195ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 196ab1b97feSMatt Spinler attributes); 197ab1b97feSMatt Spinler 19844893cc9SMatt Spinler _lastPelID = pel->id(); 19944893cc9SMatt Spinler 200b188f78aSMatt Spinler updateRepoStats(attributes, true); 201b188f78aSMatt Spinler 202ab1b97feSMatt Spinler processAddCallbacks(*pel); 203ab1b97feSMatt Spinler } 204ab1b97feSMatt Spinler 205ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 206ab1b97feSMatt Spinler { 20789fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 20889fa082aSMatt Spinler 20989fa082aSMatt Spinler if (!file.good()) 21089fa082aSMatt Spinler { 21189fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 21289fa082aSMatt Spinler // we could successfully create yet another error log here. 21389fa082aSMatt Spinler auto e = errno; 21489fa082aSMatt Spinler fs::remove(path); 215db3f2798SMatt Spinler lg2::error( 216db3f2798SMatt Spinler "Unable to open PEL file {FILE} for writing, errno = {ERRNO}", 217db3f2798SMatt Spinler "FILE", path, "ERRNO", e); 21889fa082aSMatt Spinler throw file_error::Open(); 21989fa082aSMatt Spinler } 22089fa082aSMatt Spinler 221ab1b97feSMatt Spinler auto data = pel.data(); 22289fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 22389fa082aSMatt Spinler 22489fa082aSMatt Spinler if (file.fail()) 22589fa082aSMatt Spinler { 22689fa082aSMatt Spinler // Same note as above about not being able to create an error log 22789fa082aSMatt Spinler // for this case even if we wanted. 22889fa082aSMatt Spinler auto e = errno; 22989fa082aSMatt Spinler file.close(); 23089fa082aSMatt Spinler fs::remove(path); 231db3f2798SMatt Spinler lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE", 232db3f2798SMatt Spinler path, "ERRNO", e); 23389fa082aSMatt Spinler throw file_error::Write(); 23489fa082aSMatt Spinler } 235475e574dSMatt Spinler } 236475e574dSMatt Spinler 23752602e35SMatt Spinler std::optional<Repository::LogID> Repository::remove(const LogID& id) 238475e574dSMatt Spinler { 239475e574dSMatt Spinler auto pel = findPEL(id); 240ff6b598bSPatrick Williams if (pel == _pelAttributes.end()) 241475e574dSMatt Spinler { 242ff6b598bSPatrick Williams return std::nullopt; 243ff6b598bSPatrick Williams } 244ff6b598bSPatrick Williams 245ff6b598bSPatrick Williams LogID actualID = pel->first; 246b188f78aSMatt Spinler updateRepoStats(pel->second, false); 247b188f78aSMatt Spinler 248db3f2798SMatt Spinler lg2::debug( 249db3f2798SMatt Spinler "Removing PEL from repository, PEL ID = {PEL_ID}, BMC log ID = {BMC_ID}", 250db3f2798SMatt Spinler "PEL_ID", lg2::hex, actualID.pelID.id, "BMC_ID", actualID.obmcID.id); 2511d8835bbSSumit Kumar 2521d8835bbSSumit Kumar if (fs::exists(pel->second.path)) 2531d8835bbSSumit Kumar { 2541d8835bbSSumit Kumar // Check for existense of new archive folder 2551d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 2561d8835bbSSumit Kumar { 2571d8835bbSSumit Kumar fs::create_directories(_archivePath); 2581d8835bbSSumit Kumar } 2591d8835bbSSumit Kumar 2601d8835bbSSumit Kumar // Move log file to archive folder 2611d8835bbSSumit Kumar auto fileName = _archivePath / pel->second.path.filename(); 2621d8835bbSSumit Kumar fs::rename(pel->second.path, fileName); 2631d8835bbSSumit Kumar 2641d8835bbSSumit Kumar // Update size of file 2651d8835bbSSumit Kumar _archiveSize += getFileDiskSize(fileName); 2661d8835bbSSumit Kumar } 2671d8835bbSSumit Kumar 2680ff00485SMatt Spinler _pelAttributes.erase(pel); 269421f6531SMatt Spinler 270ff6b598bSPatrick Williams processDeleteCallbacks(actualID.pelID.id); 27152602e35SMatt Spinler 27252602e35SMatt Spinler return actualID; 27389fa082aSMatt Spinler } 27489fa082aSMatt Spinler 2752813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 2762813f36dSMatt Spinler { 2772813f36dSMatt Spinler auto pel = findPEL(id); 2780ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2792813f36dSMatt Spinler { 2800ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 2812813f36dSMatt Spinler if (!file.good()) 2822813f36dSMatt Spinler { 2832813f36dSMatt Spinler auto e = errno; 284db3f2798SMatt Spinler lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}", 285db3f2798SMatt Spinler "FILE", pel->second.path, "ERRNO", e); 2862813f36dSMatt Spinler throw file_error::Open(); 2872813f36dSMatt Spinler } 2882813f36dSMatt Spinler 2892813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2902813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2912813f36dSMatt Spinler return data; 2922813f36dSMatt Spinler } 2932813f36dSMatt Spinler 2942813f36dSMatt Spinler return std::nullopt; 2952813f36dSMatt Spinler } 2962813f36dSMatt Spinler 2976d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 2986d51224bSMatt Spinler { 2996d51224bSMatt Spinler auto pel = findPEL(id); 3006d51224bSMatt Spinler if (pel != _pelAttributes.end()) 3016d51224bSMatt Spinler { 30232a6df6cSMatt Spinler int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK); 30332a6df6cSMatt Spinler if (fd == -1) 3046d51224bSMatt Spinler { 3056d51224bSMatt Spinler auto e = errno; 306db3f2798SMatt Spinler lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}", 307db3f2798SMatt Spinler "FILE", pel->second.path, "ERRNO", e); 3086d51224bSMatt Spinler throw file_error::Open(); 3096d51224bSMatt Spinler } 3106d51224bSMatt Spinler 3116d51224bSMatt Spinler // Must leave the file open here. It will be closed by sdbusplus 3126d51224bSMatt Spinler // when it sends it back over D-Bus. 31332a6df6cSMatt Spinler return fd; 3146d51224bSMatt Spinler } 3156d51224bSMatt Spinler return std::nullopt; 3166d51224bSMatt Spinler } 3176d51224bSMatt Spinler 3181ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 3191ea78801SMatt Spinler { 3200ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 3211ea78801SMatt Spinler { 3220ff00485SMatt Spinler std::ifstream file{attributes.path}; 3231ea78801SMatt Spinler 3241ea78801SMatt Spinler if (!file.good()) 3251ea78801SMatt Spinler { 3261ea78801SMatt Spinler auto e = errno; 327db3f2798SMatt Spinler lg2::error( 328db3f2798SMatt Spinler "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}", 329db3f2798SMatt Spinler "FILE", attributes.path, "ERRNO", e); 3301ea78801SMatt Spinler continue; 3311ea78801SMatt Spinler } 3321ea78801SMatt Spinler 3331ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 3341ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 3351ea78801SMatt Spinler file.close(); 3361ea78801SMatt Spinler 3371ea78801SMatt Spinler PEL pel{data}; 3381ea78801SMatt Spinler 3391ea78801SMatt Spinler try 3401ea78801SMatt Spinler { 3411ea78801SMatt Spinler if (func(pel)) 3421ea78801SMatt Spinler { 3431ea78801SMatt Spinler break; 3441ea78801SMatt Spinler } 3451ea78801SMatt Spinler } 34666491c61SPatrick Williams catch (const std::exception& e) 3471ea78801SMatt Spinler { 348db3f2798SMatt Spinler lg2::error("Repository::for_each function exception: {ERROR}", 349db3f2798SMatt Spinler "ERROR", e); 3501ea78801SMatt Spinler } 3511ea78801SMatt Spinler } 3521ea78801SMatt Spinler } 3531ea78801SMatt Spinler 354421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 355421f6531SMatt Spinler { 356421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 357421f6531SMatt Spinler { 358421f6531SMatt Spinler try 359421f6531SMatt Spinler { 360421f6531SMatt Spinler func(pel); 361421f6531SMatt Spinler } 36266491c61SPatrick Williams catch (const std::exception& e) 363421f6531SMatt Spinler { 364db3f2798SMatt Spinler lg2::error( 365db3f2798SMatt Spinler "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}", 366db3f2798SMatt Spinler "NAME", name, "ERROR", e); 367421f6531SMatt Spinler } 368421f6531SMatt Spinler } 369421f6531SMatt Spinler } 370421f6531SMatt Spinler 371421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 372421f6531SMatt Spinler { 373421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 374421f6531SMatt Spinler { 375421f6531SMatt Spinler try 376421f6531SMatt Spinler { 377421f6531SMatt Spinler func(id); 378421f6531SMatt Spinler } 37966491c61SPatrick Williams catch (const std::exception& e) 380421f6531SMatt Spinler { 381db3f2798SMatt Spinler lg2::error( 382db3f2798SMatt Spinler "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}", 383db3f2798SMatt Spinler "NAME", name, "ERROR", e); 384421f6531SMatt Spinler } 385421f6531SMatt Spinler } 386421f6531SMatt Spinler } 387421f6531SMatt Spinler 3880ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3890ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3900ff00485SMatt Spinler { 3910ff00485SMatt Spinler auto pel = findPEL(id); 3920ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3930ff00485SMatt Spinler { 3940ff00485SMatt Spinler return pel->second; 3950ff00485SMatt Spinler } 3960ff00485SMatt Spinler 3970ff00485SMatt Spinler return std::nullopt; 3980ff00485SMatt Spinler } 3990ff00485SMatt Spinler 40029d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 40129d18c11SMatt Spinler { 40229d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 40329d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 40429d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 40529d18c11SMatt Spinler 40629d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 40729d18c11SMatt Spinler { 40829d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 40929d18c11SMatt Spinler pel.setHostTransmissionState(state); 410d0ccda3cSMatt Spinler return true; 41129d18c11SMatt Spinler }; 41229d18c11SMatt Spinler 41329d18c11SMatt Spinler try 41429d18c11SMatt Spinler { 41529d18c11SMatt Spinler updatePEL(attr->second.path, func); 41629d18c11SMatt Spinler } 41766491c61SPatrick Williams catch (const std::exception& e) 41829d18c11SMatt Spinler { 419db3f2798SMatt Spinler lg2::error( 420db3f2798SMatt Spinler "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}", 421db3f2798SMatt Spinler "PATH", attr->second.path, "ERROR", e); 42229d18c11SMatt Spinler } 42329d18c11SMatt Spinler } 42429d18c11SMatt Spinler } 42529d18c11SMatt Spinler 42629d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 42729d18c11SMatt Spinler { 42829d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 42929d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 43029d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 43129d18c11SMatt Spinler 43229d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 43329d18c11SMatt Spinler { 43429d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 43529d18c11SMatt Spinler pel.setHMCTransmissionState(state); 436d0ccda3cSMatt Spinler return true; 43729d18c11SMatt Spinler }; 43829d18c11SMatt Spinler 43929d18c11SMatt Spinler try 44029d18c11SMatt Spinler { 44129d18c11SMatt Spinler updatePEL(attr->second.path, func); 44229d18c11SMatt Spinler } 44366491c61SPatrick Williams catch (const std::exception& e) 44429d18c11SMatt Spinler { 445db3f2798SMatt Spinler lg2::error( 446db3f2798SMatt Spinler "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}", 447db3f2798SMatt Spinler "PATH", attr->second.path, "ERROR", e); 44829d18c11SMatt Spinler } 44929d18c11SMatt Spinler } 45029d18c11SMatt Spinler } 45129d18c11SMatt Spinler 452*1cb59f70SMatt Spinler bool Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 45329d18c11SMatt Spinler { 45429d18c11SMatt Spinler std::ifstream file{path}; 45529d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 45629d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 45729d18c11SMatt Spinler file.close(); 45829d18c11SMatt Spinler 45929d18c11SMatt Spinler PEL pel{data}; 46029d18c11SMatt Spinler 46129d18c11SMatt Spinler if (pel.valid()) 46229d18c11SMatt Spinler { 463d0ccda3cSMatt Spinler if (updateFunc(pel)) 464d0ccda3cSMatt Spinler { 465d0ccda3cSMatt Spinler // Three attribute fields can change post creation from 466d0ccda3cSMatt Spinler // an updatePEL call: 467d0ccda3cSMatt Spinler // - hmcTransmissionState - When HMC acks a PEL 468d0ccda3cSMatt Spinler // - hostTransmissionState - When host acks a PEL 469d0ccda3cSMatt Spinler // - deconfig flag - Can be cleared for PELs that call out 470d0ccda3cSMatt Spinler // hotplugged FRUs. 471d0ccda3cSMatt Spinler // Make sure they're up to date. 472d0ccda3cSMatt Spinler LogID id{LogID::Pel(pel.id())}; 473d0ccda3cSMatt Spinler auto attr = 474d0ccda3cSMatt Spinler std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 475d0ccda3cSMatt Spinler [&id](const auto& a) { return a.first == id; }); 476d0ccda3cSMatt Spinler if (attr != _pelAttributes.end()) 477d0ccda3cSMatt Spinler { 478d0ccda3cSMatt Spinler attr->second.hmcState = pel.hmcTransmissionState(); 479d0ccda3cSMatt Spinler attr->second.hostState = pel.hostTransmissionState(); 480d0ccda3cSMatt Spinler attr->second.deconfig = pel.getDeconfigFlag(); 481d0ccda3cSMatt Spinler } 48229d18c11SMatt Spinler 48329d18c11SMatt Spinler write(pel, path); 484*1cb59f70SMatt Spinler return true; 48529d18c11SMatt Spinler } 486d0ccda3cSMatt Spinler } 48729d18c11SMatt Spinler else 48829d18c11SMatt Spinler { 48929d18c11SMatt Spinler throw std::runtime_error( 49029d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 49129d18c11SMatt Spinler } 492*1cb59f70SMatt Spinler return false; 49329d18c11SMatt Spinler } 49429d18c11SMatt Spinler 495b188f78aSMatt Spinler bool Repository::isServiceableSev(const PELAttributes& pel) 496b188f78aSMatt Spinler { 497b188f78aSMatt Spinler auto sevType = static_cast<SeverityType>(pel.severity & 0xF0); 4982544b419SPatrick Williams auto sevPVEntry = pel_values::findByValue(pel.severity, 4992544b419SPatrick Williams pel_values::severityValues); 500b188f78aSMatt Spinler std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry); 501b188f78aSMatt Spinler 502b188f78aSMatt Spinler bool check1 = (sevType == SeverityType::predictive) || 503b188f78aSMatt Spinler (sevType == SeverityType::unrecoverable) || 504b188f78aSMatt Spinler (sevType == SeverityType::critical); 505b188f78aSMatt Spinler 506b188f78aSMatt Spinler bool check2 = ((sevType == SeverityType::recovered) || 507b188f78aSMatt Spinler (sevName == "symptom_recovered")) && 508b188f78aSMatt Spinler !pel.actionFlags.test(hiddenFlagBit); 509b188f78aSMatt Spinler 510b188f78aSMatt Spinler bool check3 = (sevName == "symptom_predictive") || 511b188f78aSMatt Spinler (sevName == "symptom_unrecoverable") || 512b188f78aSMatt Spinler (sevName == "symptom_critical"); 513b188f78aSMatt Spinler 514b188f78aSMatt Spinler return check1 || check2 || check3; 515b188f78aSMatt Spinler } 516b188f78aSMatt Spinler 517b188f78aSMatt Spinler void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded) 518b188f78aSMatt Spinler { 519b188f78aSMatt Spinler auto isServiceable = Repository::isServiceableSev(pel); 520b188f78aSMatt Spinler auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator); 521b188f78aSMatt Spinler 522b188f78aSMatt Spinler auto adjustSize = [pelAdded, &pel](auto& runningSize) { 523b188f78aSMatt Spinler if (pelAdded) 524b188f78aSMatt Spinler { 525b188f78aSMatt Spinler runningSize += pel.sizeOnDisk; 526b188f78aSMatt Spinler } 527b188f78aSMatt Spinler else 528b188f78aSMatt Spinler { 529b188f78aSMatt Spinler runningSize = std::max(static_cast<int64_t>(runningSize) - 530b188f78aSMatt Spinler static_cast<int64_t>(pel.sizeOnDisk), 531b188f78aSMatt Spinler static_cast<int64_t>(0)); 532b188f78aSMatt Spinler } 533b188f78aSMatt Spinler }; 534b188f78aSMatt Spinler 535b188f78aSMatt Spinler adjustSize(_sizes.total); 536b188f78aSMatt Spinler 537b188f78aSMatt Spinler if (bmcPEL) 538b188f78aSMatt Spinler { 539b188f78aSMatt Spinler adjustSize(_sizes.bmc); 540b188f78aSMatt Spinler if (isServiceable) 541b188f78aSMatt Spinler { 542b188f78aSMatt Spinler adjustSize(_sizes.bmcServiceable); 543b188f78aSMatt Spinler } 544b188f78aSMatt Spinler else 545b188f78aSMatt Spinler { 546b188f78aSMatt Spinler adjustSize(_sizes.bmcInfo); 547b188f78aSMatt Spinler } 548b188f78aSMatt Spinler } 549b188f78aSMatt Spinler else 550b188f78aSMatt Spinler { 551b188f78aSMatt Spinler adjustSize(_sizes.nonBMC); 552b188f78aSMatt Spinler if (isServiceable) 553b188f78aSMatt Spinler { 554b188f78aSMatt Spinler adjustSize(_sizes.nonBMCServiceable); 555b188f78aSMatt Spinler } 556b188f78aSMatt Spinler else 557b188f78aSMatt Spinler { 558b188f78aSMatt Spinler adjustSize(_sizes.nonBMCInfo); 559b188f78aSMatt Spinler } 560b188f78aSMatt Spinler } 561b188f78aSMatt Spinler } 562b188f78aSMatt Spinler 563c296692bSSumit Kumar bool Repository::sizeWarning() 5647e727a39SMatt Spinler { 565c296692bSSumit Kumar std::error_code ec; 566c296692bSSumit Kumar 5671d8835bbSSumit Kumar if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) > 5681d8835bbSSumit Kumar ((_maxRepoSize * warningPercentage) / 100))) 5691d8835bbSSumit Kumar { 570db3f2798SMatt Spinler lg2::info( 5711d8835bbSSumit Kumar "Repository::sizeWarning function:Deleting the files in archive"); 5721d8835bbSSumit Kumar 573c296692bSSumit Kumar for (const auto& dirEntry : fs::directory_iterator(_archivePath)) 5741d8835bbSSumit Kumar { 575c296692bSSumit Kumar fs::remove(dirEntry.path(), ec); 576c296692bSSumit Kumar if (ec) 577c296692bSSumit Kumar { 578db3f2798SMatt Spinler lg2::info("Repository::sizeWarning: Could not delete " 579db3f2798SMatt Spinler "file {FILE} in PEL archive", 580db3f2798SMatt Spinler "FILE", dirEntry.path()); 5811d8835bbSSumit Kumar } 5821d8835bbSSumit Kumar } 5831d8835bbSSumit Kumar 584c296692bSSumit Kumar _archiveSize = 0; 585c296692bSSumit Kumar } 586c296692bSSumit Kumar 5877e727a39SMatt Spinler return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 5887e727a39SMatt Spinler (_pelAttributes.size() > _maxNumPELs); 5897e727a39SMatt Spinler } 5907e727a39SMatt Spinler 591b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> 592b0a8df5bSMatt Spinler Repository::getAllPELAttributes(SortOrder order) const 593b0a8df5bSMatt Spinler { 594b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> attributes; 595b0a8df5bSMatt Spinler 596ac1ba3f2SPatrick Williams std::for_each(_pelAttributes.begin(), _pelAttributes.end(), 597ac1ba3f2SPatrick Williams [&attributes](auto& pelEntry) { 598ac1ba3f2SPatrick Williams attributes.push_back(pelEntry); 599ac1ba3f2SPatrick Williams }); 600b0a8df5bSMatt Spinler 601b0a8df5bSMatt Spinler std::sort(attributes.begin(), attributes.end(), 602b0a8df5bSMatt Spinler [order](const auto& left, const auto& right) { 603b0a8df5bSMatt Spinler if (order == SortOrder::ascending) 604b0a8df5bSMatt Spinler { 605b0a8df5bSMatt Spinler return left.get().second.path < right.get().second.path; 606b0a8df5bSMatt Spinler } 607b0a8df5bSMatt Spinler return left.get().second.path > right.get().second.path; 608b0a8df5bSMatt Spinler }); 609b0a8df5bSMatt Spinler 610b0a8df5bSMatt Spinler return attributes; 611b0a8df5bSMatt Spinler } 612b0a8df5bSMatt Spinler 613027bf285SSumit Kumar std::vector<uint32_t> 614027bf285SSumit Kumar Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry) 615b0a8df5bSMatt Spinler { 616b0a8df5bSMatt Spinler std::vector<uint32_t> obmcLogIDs; 617db3f2798SMatt Spinler lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has " 618db3f2798SMatt Spinler "{NUM_PELS} PELs", 619db3f2798SMatt Spinler "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size()); 620b0a8df5bSMatt Spinler 621b0a8df5bSMatt Spinler // Set up the 5 functions to check if the PEL category 622b0a8df5bSMatt Spinler // is still over its limits. 623b0a8df5bSMatt Spinler 624b0a8df5bSMatt Spinler // BMC informational PELs should only take up 15% 625b0a8df5bSMatt Spinler IsOverLimitFunc overBMCInfoLimit = [this]() { 626b0a8df5bSMatt Spinler return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 627b0a8df5bSMatt Spinler }; 628b0a8df5bSMatt Spinler 629b0a8df5bSMatt Spinler // BMC non informational PELs should only take up 30% 630b0a8df5bSMatt Spinler IsOverLimitFunc overBMCNonInfoLimit = [this]() { 631b0a8df5bSMatt Spinler return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 632b0a8df5bSMatt Spinler }; 633b0a8df5bSMatt Spinler 634b0a8df5bSMatt Spinler // Non BMC informational PELs should only take up 15% 635b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCInfoLimit = [this]() { 636b0a8df5bSMatt Spinler return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 637b0a8df5bSMatt Spinler }; 638b0a8df5bSMatt Spinler 639b0a8df5bSMatt Spinler // Non BMC non informational PELs should only take up 15% 640b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 641b0a8df5bSMatt Spinler return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 642b0a8df5bSMatt Spinler }; 643b0a8df5bSMatt Spinler 644b0a8df5bSMatt Spinler // Bring the total number of PELs down to 80% of the max 645b0a8df5bSMatt Spinler IsOverLimitFunc tooManyPELsLimit = [this]() { 646b0a8df5bSMatt Spinler return _pelAttributes.size() > _maxNumPELs * 80 / 100; 647b0a8df5bSMatt Spinler }; 648b0a8df5bSMatt Spinler 649b0a8df5bSMatt Spinler // Set up the functions to determine which category a PEL is in. 650b0a8df5bSMatt Spinler // TODO: Return false in these functions if a PEL caused a guard record. 651b0a8df5bSMatt Spinler 652b0a8df5bSMatt Spinler // A BMC informational PEL 653b0a8df5bSMatt Spinler IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 654b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 655b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 656b0a8df5bSMatt Spinler }; 657b0a8df5bSMatt Spinler 658b0a8df5bSMatt Spinler // A BMC non informational PEL 659b0a8df5bSMatt Spinler IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 660b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 661b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 662b0a8df5bSMatt Spinler }; 663b0a8df5bSMatt Spinler 664b0a8df5bSMatt Spinler // A non BMC informational PEL 665b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 666b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 667b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 668b0a8df5bSMatt Spinler }; 669b0a8df5bSMatt Spinler 670b0a8df5bSMatt Spinler // A non BMC non informational PEL 671b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 672b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 673b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 674b0a8df5bSMatt Spinler }; 675b0a8df5bSMatt Spinler 676b0a8df5bSMatt Spinler // When counting PELs, count every PEL 677d26fa3e7SPatrick Williams IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; }; 678b0a8df5bSMatt Spinler 679b0a8df5bSMatt Spinler // Check all 4 categories, which will result in at most 90% 680b0a8df5bSMatt Spinler // usage (15 + 30 + 15 + 30). 681027bf285SSumit Kumar removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs); 682027bf285SSumit Kumar removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry, 683027bf285SSumit Kumar obmcLogIDs); 684027bf285SSumit Kumar removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry, 685027bf285SSumit Kumar obmcLogIDs); 686027bf285SSumit Kumar removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry, 687027bf285SSumit Kumar obmcLogIDs); 688b0a8df5bSMatt Spinler 689b0a8df5bSMatt Spinler // After the above pruning check if there are still too many PELs, 690b0a8df5bSMatt Spinler // which can happen depending on PEL sizes. 691b0a8df5bSMatt Spinler if (_pelAttributes.size() > _maxNumPELs) 692b0a8df5bSMatt Spinler { 693027bf285SSumit Kumar removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs); 694b0a8df5bSMatt Spinler } 695b0a8df5bSMatt Spinler 696b0a8df5bSMatt Spinler if (!obmcLogIDs.empty()) 697b0a8df5bSMatt Spinler { 698db3f2798SMatt Spinler lg2::info("Number of PELs removed to save space: {NUM_PELS}", 699db3f2798SMatt Spinler "NUM_PELS", obmcLogIDs.size()); 700b0a8df5bSMatt Spinler } 701b0a8df5bSMatt Spinler 702b0a8df5bSMatt Spinler return obmcLogIDs; 703b0a8df5bSMatt Spinler } 704b0a8df5bSMatt Spinler 70545796e82SMatt Spinler void Repository::removePELs(const IsOverLimitFunc& isOverLimit, 70645796e82SMatt Spinler const IsPELTypeFunc& isPELType, 707027bf285SSumit Kumar const std::vector<uint32_t>& idsWithHwIsoEntry, 708b0a8df5bSMatt Spinler std::vector<uint32_t>& removedBMCLogIDs) 709b0a8df5bSMatt Spinler { 710b0a8df5bSMatt Spinler if (!isOverLimit()) 711b0a8df5bSMatt Spinler { 712b0a8df5bSMatt Spinler return; 713b0a8df5bSMatt Spinler } 714b0a8df5bSMatt Spinler 715b0a8df5bSMatt Spinler auto attributes = getAllPELAttributes(SortOrder::ascending); 716b0a8df5bSMatt Spinler 717b0a8df5bSMatt Spinler // Make 4 passes on the PELs, stopping as soon as isOverLimit 718b0a8df5bSMatt Spinler // returns false. 719b0a8df5bSMatt Spinler // Pass 1: only delete HMC acked PELs 720b0a8df5bSMatt Spinler // Pass 2: only delete OS acked PELs 721b0a8df5bSMatt Spinler // Pass 3: only delete PHYP sent PELs 722b0a8df5bSMatt Spinler // Pass 4: delete all PELs 723b0a8df5bSMatt Spinler static const std::vector<std::function<bool(const PELAttributes& pel)>> 724b0a8df5bSMatt Spinler stateChecks{[](const auto& pel) { 725b0a8df5bSMatt Spinler return pel.hmcState == TransmissionState::acked; 726b0a8df5bSMatt Spinler }, 727b0a8df5bSMatt Spinler 728b0a8df5bSMatt Spinler [](const auto& pel) { 729b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::acked; 730b0a8df5bSMatt Spinler }, 731b0a8df5bSMatt Spinler 732b0a8df5bSMatt Spinler [](const auto& pel) { 733b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::sent; 734b0a8df5bSMatt Spinler }, 735b0a8df5bSMatt Spinler 736d26fa3e7SPatrick Williams [](const auto& /*pel*/) { return true; }}; 737b0a8df5bSMatt Spinler 738b0a8df5bSMatt Spinler for (const auto& stateCheck : stateChecks) 739b0a8df5bSMatt Spinler { 740b0a8df5bSMatt Spinler for (auto it = attributes.begin(); it != attributes.end();) 741b0a8df5bSMatt Spinler { 742b0a8df5bSMatt Spinler const auto& pel = it->get(); 743b0a8df5bSMatt Spinler if (isPELType(pel.second) && stateCheck(pel.second)) 744b0a8df5bSMatt Spinler { 745b0a8df5bSMatt Spinler auto removedID = pel.first.obmcID.id; 746027bf285SSumit Kumar 747027bf285SSumit Kumar auto idFound = std::find(idsWithHwIsoEntry.begin(), 748027bf285SSumit Kumar idsWithHwIsoEntry.end(), removedID); 749027bf285SSumit Kumar if (idFound != idsWithHwIsoEntry.end()) 750027bf285SSumit Kumar { 751027bf285SSumit Kumar ++it; 752027bf285SSumit Kumar continue; 753027bf285SSumit Kumar } 754027bf285SSumit Kumar 755b0a8df5bSMatt Spinler remove(pel.first); 756b0a8df5bSMatt Spinler 757b0a8df5bSMatt Spinler removedBMCLogIDs.push_back(removedID); 758b0a8df5bSMatt Spinler 759b0a8df5bSMatt Spinler attributes.erase(it); 760b0a8df5bSMatt Spinler 761b0a8df5bSMatt Spinler if (!isOverLimit()) 762b0a8df5bSMatt Spinler { 763b0a8df5bSMatt Spinler break; 764b0a8df5bSMatt Spinler } 765b0a8df5bSMatt Spinler } 766b0a8df5bSMatt Spinler else 767b0a8df5bSMatt Spinler { 768b0a8df5bSMatt Spinler ++it; 769b0a8df5bSMatt Spinler } 770b0a8df5bSMatt Spinler } 771b0a8df5bSMatt Spinler 772b0a8df5bSMatt Spinler if (!isOverLimit()) 773b0a8df5bSMatt Spinler { 774b0a8df5bSMatt Spinler break; 775b0a8df5bSMatt Spinler } 776b0a8df5bSMatt Spinler } 777b0a8df5bSMatt Spinler } 778b0a8df5bSMatt Spinler 7792ccdcef9SSumit Kumar void Repository::archivePEL(const PEL& pel) 7802ccdcef9SSumit Kumar { 7812ccdcef9SSumit Kumar if (pel.valid()) 7822ccdcef9SSumit Kumar { 7832ccdcef9SSumit Kumar auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime()); 7842ccdcef9SSumit Kumar 7852ccdcef9SSumit Kumar write(pel, path); 7862ccdcef9SSumit Kumar 7872ccdcef9SSumit Kumar _archiveSize += getFileDiskSize(path); 7882ccdcef9SSumit Kumar } 7892ccdcef9SSumit Kumar } 7902ccdcef9SSumit Kumar 79189fa082aSMatt Spinler } // namespace pels 79289fa082aSMatt Spinler } // namespace openpower 793