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