blob: b15fd38f0516ad5b23ea49aebedb91e6f2ea261f [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM 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 */
Matt Spinler89fa0822019-07-17 13:54:30 -050016#include "repository.hpp"
17
18#include <fstream>
19#include <phosphor-logging/log.hpp>
20#include <xyz/openbmc_project/Common/File/error.hpp>
21
22namespace openpower
23{
24namespace pels
25{
26
27namespace fs = std::filesystem;
28using namespace phosphor::logging;
29namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
30
31Repository::Repository(const std::filesystem::path& basePath) :
32 _logPath(basePath / "logs")
33{
34 if (!fs::exists(_logPath))
35 {
36 fs::create_directories(_logPath);
37 }
Matt Spinler475e5742019-07-18 16:09:49 -050038
39 restore();
40}
41
42void Repository::restore()
43{
44 for (auto& dirEntry : fs::directory_iterator(_logPath))
45 {
46 try
47 {
48 if (!fs::is_regular_file(dirEntry.path()))
49 {
50 continue;
51 }
52
53 std::ifstream file{dirEntry.path()};
54 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
55 std::istreambuf_iterator<char>()};
56 file.close();
57
Matt Spinler07eefc52019-09-26 11:18:26 -050058 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050059 if (pel.valid())
60 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060061 // If the host hasn't acked it, reset the host state so
62 // it will get sent up again.
63 if (pel.hostTransmissionState() == TransmissionState::sent)
64 {
65 pel.setHostTransmissionState(TransmissionState::newPEL);
66 try
67 {
68 write(pel, dirEntry.path());
69 }
70 catch (std::exception& e)
71 {
72 log<level::ERR>(
73 "Failed to save PEL after updating host state",
74 entry("PELID=0x%X", pel.id()));
75 }
76 }
77
Matt Spinler346f99a2019-11-21 13:06:35 -060078 PELAttributes attributes{
79 dirEntry.path(), pel.userHeader().actionFlags(),
80 pel.hostTransmissionState(), pel.hmcTransmissionState()};
Matt Spinler0ff00482019-11-06 16:19:46 -060081
Matt Spinler475e5742019-07-18 16:09:49 -050082 using pelID = LogID::Pel;
83 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -060084 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -050085 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -060086 attributes);
Matt Spinler475e5742019-07-18 16:09:49 -050087 }
88 else
89 {
90 log<level::ERR>(
91 "Found invalid PEL file while restoring. Removing.",
92 entry("FILENAME=%s", dirEntry.path().c_str()));
93 fs::remove(dirEntry.path());
94 }
95 }
96 catch (std::exception& e)
97 {
98 log<level::ERR>("Hit exception while restoring PEL File",
99 entry("FILENAME=%s", dirEntry.path().c_str()),
100 entry("ERROR=%s", e.what()));
101 }
102 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500103}
104
105std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
106{
107 char name[50];
108 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
109 time.yearLSB, time.month, time.day, time.hour, time.minutes,
110 time.seconds, time.hundredths, pelID);
111 return std::string{name};
112}
113
114void Repository::add(std::unique_ptr<PEL>& pel)
115{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600116 pel->setHostTransmissionState(TransmissionState::newPEL);
117 pel->setHMCTransmissionState(TransmissionState::newPEL);
118
Matt Spinler89fa0822019-07-17 13:54:30 -0500119 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600120
121 write(*(pel.get()), path);
122
Matt Spinler346f99a2019-11-21 13:06:35 -0600123 PELAttributes attributes{path, pel->userHeader().actionFlags(),
124 pel->hostTransmissionState(),
125 pel->hmcTransmissionState()};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600126
127 using pelID = LogID::Pel;
128 using obmcID = LogID::Obmc;
129 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
130 attributes);
131
132 processAddCallbacks(*pel);
133}
134
135void Repository::write(const PEL& pel, const fs::path& path)
136{
Matt Spinler89fa0822019-07-17 13:54:30 -0500137 std::ofstream file{path, std::ios::binary};
138
139 if (!file.good())
140 {
141 // If this fails, the filesystem is probably full so it isn't like
142 // we could successfully create yet another error log here.
143 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500144 fs::remove(path);
145 log<level::ERR>("Unable to open PEL file for writing",
146 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
147 throw file_error::Open();
148 }
149
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600150 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500151 file.write(reinterpret_cast<const char*>(data.data()), data.size());
152
153 if (file.fail())
154 {
155 // Same note as above about not being able to create an error log
156 // for this case even if we wanted.
157 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500158 file.close();
159 fs::remove(path);
160 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
161 entry("PATH=%s", path.c_str()));
162 throw file_error::Write();
163 }
Matt Spinler475e5742019-07-18 16:09:49 -0500164}
165
166void Repository::remove(const LogID& id)
167{
168 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600169 if (pel != _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500170 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600171 fs::remove(pel->second.path);
172 _pelAttributes.erase(pel);
Matt Spinler421f6532019-11-06 15:40:45 -0600173
174 processDeleteCallbacks(id.pelID.id);
Matt Spinler475e5742019-07-18 16:09:49 -0500175 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500176}
177
Matt Spinler2813f362019-07-19 12:45:28 -0500178std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
179{
180 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600181 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500182 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600183 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500184 if (!file.good())
185 {
186 auto e = errno;
187 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600188 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500189 throw file_error::Open();
190 }
191
192 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
193 std::istreambuf_iterator<char>()};
194 return data;
195 }
196
197 return std::nullopt;
198}
199
Matt Spinler1ea78802019-11-01 13:04:59 -0500200void Repository::for_each(ForEachFunc func) const
201{
Matt Spinler0ff00482019-11-06 16:19:46 -0600202 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500203 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600204 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500205
206 if (!file.good())
207 {
208 auto e = errno;
209 log<level::ERR>("Repository::for_each: Unable to open PEL file",
210 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600211 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500212 continue;
213 }
214
215 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
216 std::istreambuf_iterator<char>()};
217 file.close();
218
219 PEL pel{data};
220
221 try
222 {
223 if (func(pel))
224 {
225 break;
226 }
227 }
228 catch (std::exception& e)
229 {
230 log<level::ERR>("Repository::for_each function exception",
231 entry("ERROR=%s", e.what()));
232 }
233 }
234}
235
Matt Spinler421f6532019-11-06 15:40:45 -0600236void Repository::processAddCallbacks(const PEL& pel) const
237{
238 for (auto& [name, func] : _addSubscriptions)
239 {
240 try
241 {
242 func(pel);
243 }
244 catch (std::exception& e)
245 {
246 log<level::ERR>("PEL Repository add callback exception",
247 entry("NAME=%s", name.c_str()),
248 entry("ERROR=%s", e.what()));
249 }
250 }
251}
252
253void Repository::processDeleteCallbacks(uint32_t id) const
254{
255 for (auto& [name, func] : _deleteSubscriptions)
256 {
257 try
258 {
259 func(id);
260 }
261 catch (std::exception& e)
262 {
263 log<level::ERR>("PEL Repository delete callback exception",
264 entry("NAME=%s", name.c_str()),
265 entry("ERROR=%s", e.what()));
266 }
267 }
268}
269
Matt Spinler0ff00482019-11-06 16:19:46 -0600270std::optional<std::reference_wrapper<const Repository::PELAttributes>>
271 Repository::getPELAttributes(const LogID& id) const
272{
273 auto pel = findPEL(id);
274 if (pel != _pelAttributes.end())
275 {
276 return pel->second;
277 }
278
279 return std::nullopt;
280}
281
Matt Spinler89fa0822019-07-17 13:54:30 -0500282} // namespace pels
283} // namespace openpower