1 #include "repository.hpp" 2 3 #include <fstream> 4 #include <phosphor-logging/log.hpp> 5 #include <xyz/openbmc_project/Common/File/error.hpp> 6 7 namespace openpower 8 { 9 namespace pels 10 { 11 12 namespace fs = std::filesystem; 13 using namespace phosphor::logging; 14 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 15 16 Repository::Repository(const std::filesystem::path& basePath) : 17 _logPath(basePath / "logs") 18 { 19 if (!fs::exists(_logPath)) 20 { 21 fs::create_directories(_logPath); 22 } 23 24 restore(); 25 } 26 27 void Repository::restore() 28 { 29 for (auto& dirEntry : fs::directory_iterator(_logPath)) 30 { 31 try 32 { 33 if (!fs::is_regular_file(dirEntry.path())) 34 { 35 continue; 36 } 37 38 std::ifstream file{dirEntry.path()}; 39 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 40 std::istreambuf_iterator<char>()}; 41 file.close(); 42 43 PEL pel(std::move(data)); 44 if (pel.valid()) 45 { 46 using pelID = LogID::Pel; 47 using obmcID = LogID::Obmc; 48 _idsToPELs.emplace( 49 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 50 dirEntry.path()); 51 } 52 else 53 { 54 log<level::ERR>( 55 "Found invalid PEL file while restoring. Removing.", 56 entry("FILENAME=%s", dirEntry.path().c_str())); 57 fs::remove(dirEntry.path()); 58 } 59 } 60 catch (std::exception& e) 61 { 62 log<level::ERR>("Hit exception while restoring PEL File", 63 entry("FILENAME=%s", dirEntry.path().c_str()), 64 entry("ERROR=%s", e.what())); 65 } 66 } 67 } 68 69 std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 70 { 71 char name[50]; 72 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 73 time.yearLSB, time.month, time.day, time.hour, time.minutes, 74 time.seconds, time.hundredths, pelID); 75 return std::string{name}; 76 } 77 78 void Repository::add(std::unique_ptr<PEL>& pel) 79 { 80 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 81 std::ofstream file{path, std::ios::binary}; 82 83 if (!file.good()) 84 { 85 // If this fails, the filesystem is probably full so it isn't like 86 // we could successfully create yet another error log here. 87 auto e = errno; 88 log<level::ERR>("Failed creating PEL file", 89 entry("FILE=%s", path.c_str())); 90 fs::remove(path); 91 log<level::ERR>("Unable to open PEL file for writing", 92 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 93 throw file_error::Open(); 94 } 95 96 auto data = pel->data(); 97 file.write(reinterpret_cast<const char*>(data.data()), data.size()); 98 99 if (file.fail()) 100 { 101 // Same note as above about not being able to create an error log 102 // for this case even if we wanted. 103 auto e = errno; 104 log<level::ERR>("Failed writing PEL file", 105 entry("FILE=%s", path.c_str())); 106 file.close(); 107 fs::remove(path); 108 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 109 entry("PATH=%s", path.c_str())); 110 throw file_error::Write(); 111 } 112 113 using pelID = LogID::Pel; 114 using obmcID = LogID::Obmc; 115 _idsToPELs.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), path); 116 } 117 118 void Repository::remove(const LogID& id) 119 { 120 auto pel = findPEL(id); 121 if (pel != _idsToPELs.end()) 122 { 123 fs::remove(pel->second); 124 _idsToPELs.erase(pel); 125 } 126 } 127 128 std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 129 { 130 auto pel = findPEL(id); 131 if (pel != _idsToPELs.end()) 132 { 133 std::ifstream file{pel->second.c_str()}; 134 if (!file.good()) 135 { 136 auto e = errno; 137 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 138 entry("PATH=%s", pel->second.c_str())); 139 throw file_error::Open(); 140 } 141 142 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 143 std::istreambuf_iterator<char>()}; 144 return data; 145 } 146 147 return std::nullopt; 148 } 149 150 } // namespace pels 151 } // namespace openpower 152