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 1889fa082aSMatt Spinler #include <fstream> 1989fa082aSMatt Spinler #include <phosphor-logging/log.hpp> 2089fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2189fa082aSMatt Spinler 2289fa082aSMatt Spinler namespace openpower 2389fa082aSMatt Spinler { 2489fa082aSMatt Spinler namespace pels 2589fa082aSMatt Spinler { 2689fa082aSMatt Spinler 2789fa082aSMatt Spinler namespace fs = std::filesystem; 2889fa082aSMatt Spinler using namespace phosphor::logging; 2989fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3089fa082aSMatt Spinler 3189fa082aSMatt Spinler Repository::Repository(const std::filesystem::path& basePath) : 3289fa082aSMatt Spinler _logPath(basePath / "logs") 3389fa082aSMatt Spinler { 3489fa082aSMatt Spinler if (!fs::exists(_logPath)) 3589fa082aSMatt Spinler { 3689fa082aSMatt Spinler fs::create_directories(_logPath); 3789fa082aSMatt Spinler } 38475e574dSMatt Spinler 39475e574dSMatt Spinler restore(); 40475e574dSMatt Spinler } 41475e574dSMatt Spinler 42475e574dSMatt Spinler void Repository::restore() 43475e574dSMatt Spinler { 44475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 45475e574dSMatt Spinler { 46475e574dSMatt Spinler try 47475e574dSMatt Spinler { 48475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 49475e574dSMatt Spinler { 50475e574dSMatt Spinler continue; 51475e574dSMatt Spinler } 52475e574dSMatt Spinler 53475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 54475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 55475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 56475e574dSMatt Spinler file.close(); 57475e574dSMatt Spinler 5807eefc54SMatt Spinler PEL pel{data}; 59475e574dSMatt Spinler if (pel.valid()) 60475e574dSMatt Spinler { 61*346f99a1SMatt Spinler PELAttributes attributes{ 62*346f99a1SMatt Spinler dirEntry.path(), pel.userHeader().actionFlags(), 63*346f99a1SMatt Spinler pel.hostTransmissionState(), pel.hmcTransmissionState()}; 640ff00485SMatt Spinler 65475e574dSMatt Spinler using pelID = LogID::Pel; 66475e574dSMatt Spinler using obmcID = LogID::Obmc; 670ff00485SMatt Spinler _pelAttributes.emplace( 68475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 690ff00485SMatt Spinler attributes); 70475e574dSMatt Spinler } 71475e574dSMatt Spinler else 72475e574dSMatt Spinler { 73475e574dSMatt Spinler log<level::ERR>( 74475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 75475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 76475e574dSMatt Spinler fs::remove(dirEntry.path()); 77475e574dSMatt Spinler } 78475e574dSMatt Spinler } 79475e574dSMatt Spinler catch (std::exception& e) 80475e574dSMatt Spinler { 81475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 82475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 83475e574dSMatt Spinler entry("ERROR=%s", e.what())); 84475e574dSMatt Spinler } 85475e574dSMatt Spinler } 8689fa082aSMatt Spinler } 8789fa082aSMatt Spinler 8889fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 8989fa082aSMatt Spinler { 9089fa082aSMatt Spinler char name[50]; 9189fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 9289fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 9389fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 9489fa082aSMatt Spinler return std::string{name}; 9589fa082aSMatt Spinler } 9689fa082aSMatt Spinler 9789fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 9889fa082aSMatt Spinler { 9989fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 100ab1b97feSMatt Spinler 101ab1b97feSMatt Spinler write(*(pel.get()), path); 102ab1b97feSMatt Spinler 103*346f99a1SMatt Spinler PELAttributes attributes{path, pel->userHeader().actionFlags(), 104*346f99a1SMatt Spinler pel->hostTransmissionState(), 105*346f99a1SMatt Spinler pel->hmcTransmissionState()}; 106ab1b97feSMatt Spinler 107ab1b97feSMatt Spinler using pelID = LogID::Pel; 108ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 109ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 110ab1b97feSMatt Spinler attributes); 111ab1b97feSMatt Spinler 112ab1b97feSMatt Spinler processAddCallbacks(*pel); 113ab1b97feSMatt Spinler } 114ab1b97feSMatt Spinler 115ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 116ab1b97feSMatt Spinler { 11789fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 11889fa082aSMatt Spinler 11989fa082aSMatt Spinler if (!file.good()) 12089fa082aSMatt Spinler { 12189fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 12289fa082aSMatt Spinler // we could successfully create yet another error log here. 12389fa082aSMatt Spinler auto e = errno; 12489fa082aSMatt Spinler fs::remove(path); 12589fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 12689fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 12789fa082aSMatt Spinler throw file_error::Open(); 12889fa082aSMatt Spinler } 12989fa082aSMatt Spinler 130ab1b97feSMatt Spinler auto data = pel.data(); 13189fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 13289fa082aSMatt Spinler 13389fa082aSMatt Spinler if (file.fail()) 13489fa082aSMatt Spinler { 13589fa082aSMatt Spinler // Same note as above about not being able to create an error log 13689fa082aSMatt Spinler // for this case even if we wanted. 13789fa082aSMatt Spinler auto e = errno; 13889fa082aSMatt Spinler file.close(); 13989fa082aSMatt Spinler fs::remove(path); 14089fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 14189fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 14289fa082aSMatt Spinler throw file_error::Write(); 14389fa082aSMatt Spinler } 144475e574dSMatt Spinler } 145475e574dSMatt Spinler 146475e574dSMatt Spinler void Repository::remove(const LogID& id) 147475e574dSMatt Spinler { 148475e574dSMatt Spinler auto pel = findPEL(id); 1490ff00485SMatt Spinler if (pel != _pelAttributes.end()) 150475e574dSMatt Spinler { 1510ff00485SMatt Spinler fs::remove(pel->second.path); 1520ff00485SMatt Spinler _pelAttributes.erase(pel); 153421f6531SMatt Spinler 154421f6531SMatt Spinler processDeleteCallbacks(id.pelID.id); 155475e574dSMatt Spinler } 15689fa082aSMatt Spinler } 15789fa082aSMatt Spinler 1582813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 1592813f36dSMatt Spinler { 1602813f36dSMatt Spinler auto pel = findPEL(id); 1610ff00485SMatt Spinler if (pel != _pelAttributes.end()) 1622813f36dSMatt Spinler { 1630ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 1642813f36dSMatt Spinler if (!file.good()) 1652813f36dSMatt Spinler { 1662813f36dSMatt Spinler auto e = errno; 1672813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 1680ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 1692813f36dSMatt Spinler throw file_error::Open(); 1702813f36dSMatt Spinler } 1712813f36dSMatt Spinler 1722813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 1732813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 1742813f36dSMatt Spinler return data; 1752813f36dSMatt Spinler } 1762813f36dSMatt Spinler 1772813f36dSMatt Spinler return std::nullopt; 1782813f36dSMatt Spinler } 1792813f36dSMatt Spinler 1801ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 1811ea78801SMatt Spinler { 1820ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 1831ea78801SMatt Spinler { 1840ff00485SMatt Spinler std::ifstream file{attributes.path}; 1851ea78801SMatt Spinler 1861ea78801SMatt Spinler if (!file.good()) 1871ea78801SMatt Spinler { 1881ea78801SMatt Spinler auto e = errno; 1891ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 1901ea78801SMatt Spinler entry("ERRNO=%d", e), 1910ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 1921ea78801SMatt Spinler continue; 1931ea78801SMatt Spinler } 1941ea78801SMatt Spinler 1951ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 1961ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 1971ea78801SMatt Spinler file.close(); 1981ea78801SMatt Spinler 1991ea78801SMatt Spinler PEL pel{data}; 2001ea78801SMatt Spinler 2011ea78801SMatt Spinler try 2021ea78801SMatt Spinler { 2031ea78801SMatt Spinler if (func(pel)) 2041ea78801SMatt Spinler { 2051ea78801SMatt Spinler break; 2061ea78801SMatt Spinler } 2071ea78801SMatt Spinler } 2081ea78801SMatt Spinler catch (std::exception& e) 2091ea78801SMatt Spinler { 2101ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 2111ea78801SMatt Spinler entry("ERROR=%s", e.what())); 2121ea78801SMatt Spinler } 2131ea78801SMatt Spinler } 2141ea78801SMatt Spinler } 2151ea78801SMatt Spinler 216421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 217421f6531SMatt Spinler { 218421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 219421f6531SMatt Spinler { 220421f6531SMatt Spinler try 221421f6531SMatt Spinler { 222421f6531SMatt Spinler func(pel); 223421f6531SMatt Spinler } 224421f6531SMatt Spinler catch (std::exception& e) 225421f6531SMatt Spinler { 226421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 227421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 228421f6531SMatt Spinler entry("ERROR=%s", e.what())); 229421f6531SMatt Spinler } 230421f6531SMatt Spinler } 231421f6531SMatt Spinler } 232421f6531SMatt Spinler 233421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 234421f6531SMatt Spinler { 235421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 236421f6531SMatt Spinler { 237421f6531SMatt Spinler try 238421f6531SMatt Spinler { 239421f6531SMatt Spinler func(id); 240421f6531SMatt Spinler } 241421f6531SMatt Spinler catch (std::exception& e) 242421f6531SMatt Spinler { 243421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 244421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 245421f6531SMatt Spinler entry("ERROR=%s", e.what())); 246421f6531SMatt Spinler } 247421f6531SMatt Spinler } 248421f6531SMatt Spinler } 249421f6531SMatt Spinler 2500ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 2510ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 2520ff00485SMatt Spinler { 2530ff00485SMatt Spinler auto pel = findPEL(id); 2540ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2550ff00485SMatt Spinler { 2560ff00485SMatt Spinler return pel->second; 2570ff00485SMatt Spinler } 2580ff00485SMatt Spinler 2590ff00485SMatt Spinler return std::nullopt; 2600ff00485SMatt Spinler } 2610ff00485SMatt Spinler 26289fa082aSMatt Spinler } // namespace pels 26389fa082aSMatt Spinler } // namespace openpower 264