blob: 331b86a26e4913659a77941103f2d403b6f884c7 [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
Matt Spinler8d5f3a22020-07-07 10:30:33 -050031Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
32 size_t maxNumPELs) :
33 _logPath(basePath / "logs"),
34 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs)
Matt Spinler89fa0822019-07-17 13:54:30 -050035{
36 if (!fs::exists(_logPath))
37 {
38 fs::create_directories(_logPath);
39 }
Matt Spinler475e5742019-07-18 16:09:49 -050040
41 restore();
42}
43
44void Repository::restore()
45{
46 for (auto& dirEntry : fs::directory_iterator(_logPath))
47 {
48 try
49 {
50 if (!fs::is_regular_file(dirEntry.path()))
51 {
52 continue;
53 }
54
55 std::ifstream file{dirEntry.path()};
56 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
57 std::istreambuf_iterator<char>()};
58 file.close();
59
Matt Spinler07eefc52019-09-26 11:18:26 -050060 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050061 if (pel.valid())
62 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060063 // If the host hasn't acked it, reset the host state so
64 // it will get sent up again.
65 if (pel.hostTransmissionState() == TransmissionState::sent)
66 {
67 pel.setHostTransmissionState(TransmissionState::newPEL);
68 try
69 {
70 write(pel, dirEntry.path());
71 }
72 catch (std::exception& e)
73 {
74 log<level::ERR>(
75 "Failed to save PEL after updating host state",
76 entry("PELID=0x%X", pel.id()));
77 }
78 }
79
Matt Spinler346f99a2019-11-21 13:06:35 -060080 PELAttributes attributes{
81 dirEntry.path(), pel.userHeader().actionFlags(),
82 pel.hostTransmissionState(), pel.hmcTransmissionState()};
Matt Spinler0ff00482019-11-06 16:19:46 -060083
Matt Spinler475e5742019-07-18 16:09:49 -050084 using pelID = LogID::Pel;
85 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -060086 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -050087 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -060088 attributes);
Matt Spinler475e5742019-07-18 16:09:49 -050089 }
90 else
91 {
92 log<level::ERR>(
93 "Found invalid PEL file while restoring. Removing.",
94 entry("FILENAME=%s", dirEntry.path().c_str()));
95 fs::remove(dirEntry.path());
96 }
97 }
98 catch (std::exception& e)
99 {
100 log<level::ERR>("Hit exception while restoring PEL File",
101 entry("FILENAME=%s", dirEntry.path().c_str()),
102 entry("ERROR=%s", e.what()));
103 }
104 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500105}
106
107std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
108{
109 char name[50];
110 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
111 time.yearLSB, time.month, time.day, time.hour, time.minutes,
112 time.seconds, time.hundredths, pelID);
113 return std::string{name};
114}
115
116void Repository::add(std::unique_ptr<PEL>& pel)
117{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600118 pel->setHostTransmissionState(TransmissionState::newPEL);
119 pel->setHMCTransmissionState(TransmissionState::newPEL);
120
Matt Spinler89fa0822019-07-17 13:54:30 -0500121 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600122
123 write(*(pel.get()), path);
124
Matt Spinler346f99a2019-11-21 13:06:35 -0600125 PELAttributes attributes{path, pel->userHeader().actionFlags(),
126 pel->hostTransmissionState(),
127 pel->hmcTransmissionState()};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600128
129 using pelID = LogID::Pel;
130 using obmcID = LogID::Obmc;
131 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
132 attributes);
133
134 processAddCallbacks(*pel);
135}
136
137void Repository::write(const PEL& pel, const fs::path& path)
138{
Matt Spinler89fa0822019-07-17 13:54:30 -0500139 std::ofstream file{path, std::ios::binary};
140
141 if (!file.good())
142 {
143 // If this fails, the filesystem is probably full so it isn't like
144 // we could successfully create yet another error log here.
145 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500146 fs::remove(path);
147 log<level::ERR>("Unable to open PEL file for writing",
148 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
149 throw file_error::Open();
150 }
151
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600152 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500153 file.write(reinterpret_cast<const char*>(data.data()), data.size());
154
155 if (file.fail())
156 {
157 // Same note as above about not being able to create an error log
158 // for this case even if we wanted.
159 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500160 file.close();
161 fs::remove(path);
162 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
163 entry("PATH=%s", path.c_str()));
164 throw file_error::Write();
165 }
Matt Spinler475e5742019-07-18 16:09:49 -0500166}
167
168void Repository::remove(const LogID& id)
169{
170 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600171 if (pel != _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500172 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600173 log<level::DEBUG>("Removing PEL from repository",
174 entry("PEL_ID=0x%X", pel->first.pelID.id),
175 entry("OBMC_LOG_ID=%d", pel->first.obmcID.id));
Matt Spinler0ff00482019-11-06 16:19:46 -0600176 fs::remove(pel->second.path);
177 _pelAttributes.erase(pel);
Matt Spinler421f6532019-11-06 15:40:45 -0600178
Matt Spinler5f5352e2020-03-05 16:23:27 -0600179 processDeleteCallbacks(pel->first.pelID.id);
180 }
181 else
182 {
183 log<level::DEBUG>("Could not find PEL to remove",
184 entry("PEL_ID=0x%X", id.pelID.id),
185 entry("OBMC_LOG_ID=%d", id.obmcID.id));
Matt Spinler475e5742019-07-18 16:09:49 -0500186 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500187}
188
Matt Spinler2813f362019-07-19 12:45:28 -0500189std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
190{
191 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600192 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500193 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600194 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500195 if (!file.good())
196 {
197 auto e = errno;
198 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600199 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500200 throw file_error::Open();
201 }
202
203 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
204 std::istreambuf_iterator<char>()};
205 return data;
206 }
207
208 return std::nullopt;
209}
210
Matt Spinler6d512242019-12-09 13:44:17 -0600211std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
212{
213 auto pel = findPEL(id);
214 if (pel != _pelAttributes.end())
215 {
216 FILE* fp = fopen(pel->second.path.c_str(), "rb");
217
218 if (fp == nullptr)
219 {
220 auto e = errno;
221 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
222 entry("PATH=%s", pel->second.path.c_str()));
223 throw file_error::Open();
224 }
225
226 // Must leave the file open here. It will be closed by sdbusplus
227 // when it sends it back over D-Bus.
228
229 return fileno(fp);
230 }
231 return std::nullopt;
232}
233
Matt Spinler1ea78802019-11-01 13:04:59 -0500234void Repository::for_each(ForEachFunc func) const
235{
Matt Spinler0ff00482019-11-06 16:19:46 -0600236 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500237 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600238 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500239
240 if (!file.good())
241 {
242 auto e = errno;
243 log<level::ERR>("Repository::for_each: Unable to open PEL file",
244 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600245 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500246 continue;
247 }
248
249 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
250 std::istreambuf_iterator<char>()};
251 file.close();
252
253 PEL pel{data};
254
255 try
256 {
257 if (func(pel))
258 {
259 break;
260 }
261 }
262 catch (std::exception& e)
263 {
264 log<level::ERR>("Repository::for_each function exception",
265 entry("ERROR=%s", e.what()));
266 }
267 }
268}
269
Matt Spinler421f6532019-11-06 15:40:45 -0600270void Repository::processAddCallbacks(const PEL& pel) const
271{
272 for (auto& [name, func] : _addSubscriptions)
273 {
274 try
275 {
276 func(pel);
277 }
278 catch (std::exception& e)
279 {
280 log<level::ERR>("PEL Repository add callback exception",
281 entry("NAME=%s", name.c_str()),
282 entry("ERROR=%s", e.what()));
283 }
284 }
285}
286
287void Repository::processDeleteCallbacks(uint32_t id) const
288{
289 for (auto& [name, func] : _deleteSubscriptions)
290 {
291 try
292 {
293 func(id);
294 }
295 catch (std::exception& e)
296 {
297 log<level::ERR>("PEL Repository delete callback exception",
298 entry("NAME=%s", name.c_str()),
299 entry("ERROR=%s", e.what()));
300 }
301 }
302}
303
Matt Spinler0ff00482019-11-06 16:19:46 -0600304std::optional<std::reference_wrapper<const Repository::PELAttributes>>
305 Repository::getPELAttributes(const LogID& id) const
306{
307 auto pel = findPEL(id);
308 if (pel != _pelAttributes.end())
309 {
310 return pel->second;
311 }
312
313 return std::nullopt;
314}
315
Matt Spinler29d18c12019-11-21 13:31:27 -0600316void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
317{
318 LogID id{LogID::Pel{pelID}};
319 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
320 [&id](const auto& a) { return a.first == id; });
321
322 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
323 {
324 PELUpdateFunc func = [state](PEL& pel) {
325 pel.setHostTransmissionState(state);
326 };
327
328 try
329 {
330 updatePEL(attr->second.path, func);
331
332 attr->second.hostState = state;
333 }
334 catch (std::exception& e)
335 {
336 log<level::ERR>("Unable to update PEL host transmission state",
337 entry("PATH=%s", attr->second.path.c_str()),
338 entry("ERROR=%s", e.what()));
339 }
340 }
341}
342
343void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
344{
345 LogID id{LogID::Pel{pelID}};
346 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
347 [&id](const auto& a) { return a.first == id; });
348
349 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
350 {
351 PELUpdateFunc func = [state](PEL& pel) {
352 pel.setHMCTransmissionState(state);
353 };
354
355 try
356 {
357 updatePEL(attr->second.path, func);
358
359 attr->second.hmcState = state;
360 }
361 catch (std::exception& e)
362 {
363 log<level::ERR>("Unable to update PEL HMC transmission state",
364 entry("PATH=%s", attr->second.path.c_str()),
365 entry("ERROR=%s", e.what()));
366 }
367 }
368}
369
370void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
371{
372 std::ifstream file{path};
373 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
374 std::istreambuf_iterator<char>()};
375 file.close();
376
377 PEL pel{data};
378
379 if (pel.valid())
380 {
381 updateFunc(pel);
382
383 write(pel, path);
384 }
385 else
386 {
387 throw std::runtime_error(
388 "Unable to read a valid PEL when trying to update it");
389 }
390}
391
Matt Spinler89fa0822019-07-17 13:54:30 -0500392} // namespace pels
393} // namespace openpower