blob: 4e406786facea50e55bef4712e98983e55bac6e2 [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
Matt Spinlerdd325c32020-07-07 11:01:54 -050018#include <sys/stat.h>
19
Matt Spinler89fa0822019-07-17 13:54:30 -050020#include <fstream>
21#include <phosphor-logging/log.hpp>
22#include <xyz/openbmc_project/Common/File/error.hpp>
23
24namespace openpower
25{
26namespace pels
27{
28
29namespace fs = std::filesystem;
30using namespace phosphor::logging;
31namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
32
Matt Spinlerdd325c32020-07-07 11:01:54 -050033/**
34 * @brief Returns the amount of space the file uses on disk.
35 *
36 * This is different than just the regular size of the file.
37 *
38 * @param[in] file - The file to get the size of
39 *
40 * @return size_t The disk space the file uses
41 */
42size_t getFileDiskSize(const std::filesystem::path& file)
43{
44 constexpr size_t statBlockSize = 512;
45 struct stat statData;
46 auto rc = stat(file.c_str(), &statData);
47 if (rc != 0)
48 {
49 auto e = errno;
50 std::string msg = "call to stat() failed on " + file.native() +
51 " with errno " + std::to_string(e);
52 log<level::ERR>(msg.c_str());
53 abort();
54 }
55
56 return statData.st_blocks * statBlockSize;
57}
58
Matt Spinler8d5f3a22020-07-07 10:30:33 -050059Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
60 size_t maxNumPELs) :
61 _logPath(basePath / "logs"),
62 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs)
Matt Spinler89fa0822019-07-17 13:54:30 -050063{
64 if (!fs::exists(_logPath))
65 {
66 fs::create_directories(_logPath);
67 }
Matt Spinler475e5742019-07-18 16:09:49 -050068
69 restore();
70}
71
72void Repository::restore()
73{
74 for (auto& dirEntry : fs::directory_iterator(_logPath))
75 {
76 try
77 {
78 if (!fs::is_regular_file(dirEntry.path()))
79 {
80 continue;
81 }
82
83 std::ifstream file{dirEntry.path()};
84 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
85 std::istreambuf_iterator<char>()};
86 file.close();
87
Matt Spinler07eefc52019-09-26 11:18:26 -050088 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050089 if (pel.valid())
90 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060091 // If the host hasn't acked it, reset the host state so
92 // it will get sent up again.
93 if (pel.hostTransmissionState() == TransmissionState::sent)
94 {
95 pel.setHostTransmissionState(TransmissionState::newPEL);
96 try
97 {
98 write(pel, dirEntry.path());
99 }
100 catch (std::exception& e)
101 {
102 log<level::ERR>(
103 "Failed to save PEL after updating host state",
104 entry("PELID=0x%X", pel.id()));
105 }
106 }
107
Matt Spinlerdd325c32020-07-07 11:01:54 -0500108 PELAttributes attributes{dirEntry.path(),
109 getFileDiskSize(dirEntry.path()),
110 pel.privateHeader().creatorID(),
111 pel.userHeader().severity(),
112 pel.userHeader().actionFlags(),
113 pel.hostTransmissionState(),
114 pel.hmcTransmissionState()};
Matt Spinler0ff00482019-11-06 16:19:46 -0600115
Matt Spinler475e5742019-07-18 16:09:49 -0500116 using pelID = LogID::Pel;
117 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600118 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500119 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600120 attributes);
Matt Spinler475e5742019-07-18 16:09:49 -0500121 }
122 else
123 {
124 log<level::ERR>(
125 "Found invalid PEL file while restoring. Removing.",
126 entry("FILENAME=%s", dirEntry.path().c_str()));
127 fs::remove(dirEntry.path());
128 }
129 }
130 catch (std::exception& e)
131 {
132 log<level::ERR>("Hit exception while restoring PEL File",
133 entry("FILENAME=%s", dirEntry.path().c_str()),
134 entry("ERROR=%s", e.what()));
135 }
136 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500137}
138
139std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
140{
141 char name[50];
142 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
143 time.yearLSB, time.month, time.day, time.hour, time.minutes,
144 time.seconds, time.hundredths, pelID);
145 return std::string{name};
146}
147
148void Repository::add(std::unique_ptr<PEL>& pel)
149{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600150 pel->setHostTransmissionState(TransmissionState::newPEL);
151 pel->setHMCTransmissionState(TransmissionState::newPEL);
152
Matt Spinler89fa0822019-07-17 13:54:30 -0500153 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600154
155 write(*(pel.get()), path);
156
Matt Spinlerdd325c32020-07-07 11:01:54 -0500157 PELAttributes attributes{path,
158 getFileDiskSize(path),
159 pel->privateHeader().creatorID(),
160 pel->userHeader().severity(),
161 pel->userHeader().actionFlags(),
Matt Spinler346f99a2019-11-21 13:06:35 -0600162 pel->hostTransmissionState(),
163 pel->hmcTransmissionState()};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600164
165 using pelID = LogID::Pel;
166 using obmcID = LogID::Obmc;
167 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
168 attributes);
169
170 processAddCallbacks(*pel);
171}
172
173void Repository::write(const PEL& pel, const fs::path& path)
174{
Matt Spinler89fa0822019-07-17 13:54:30 -0500175 std::ofstream file{path, std::ios::binary};
176
177 if (!file.good())
178 {
179 // If this fails, the filesystem is probably full so it isn't like
180 // we could successfully create yet another error log here.
181 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500182 fs::remove(path);
183 log<level::ERR>("Unable to open PEL file for writing",
184 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
185 throw file_error::Open();
186 }
187
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600188 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500189 file.write(reinterpret_cast<const char*>(data.data()), data.size());
190
191 if (file.fail())
192 {
193 // Same note as above about not being able to create an error log
194 // for this case even if we wanted.
195 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500196 file.close();
197 fs::remove(path);
198 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
199 entry("PATH=%s", path.c_str()));
200 throw file_error::Write();
201 }
Matt Spinler475e5742019-07-18 16:09:49 -0500202}
203
204void Repository::remove(const LogID& id)
205{
206 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600207 if (pel != _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500208 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600209 log<level::DEBUG>("Removing PEL from repository",
210 entry("PEL_ID=0x%X", pel->first.pelID.id),
211 entry("OBMC_LOG_ID=%d", pel->first.obmcID.id));
Matt Spinler0ff00482019-11-06 16:19:46 -0600212 fs::remove(pel->second.path);
213 _pelAttributes.erase(pel);
Matt Spinler421f6532019-11-06 15:40:45 -0600214
Matt Spinler5f5352e2020-03-05 16:23:27 -0600215 processDeleteCallbacks(pel->first.pelID.id);
216 }
217 else
218 {
219 log<level::DEBUG>("Could not find PEL to remove",
220 entry("PEL_ID=0x%X", id.pelID.id),
221 entry("OBMC_LOG_ID=%d", id.obmcID.id));
Matt Spinler475e5742019-07-18 16:09:49 -0500222 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500223}
224
Matt Spinler2813f362019-07-19 12:45:28 -0500225std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
226{
227 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600228 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500229 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600230 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500231 if (!file.good())
232 {
233 auto e = errno;
234 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600235 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500236 throw file_error::Open();
237 }
238
239 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
240 std::istreambuf_iterator<char>()};
241 return data;
242 }
243
244 return std::nullopt;
245}
246
Matt Spinler6d512242019-12-09 13:44:17 -0600247std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
248{
249 auto pel = findPEL(id);
250 if (pel != _pelAttributes.end())
251 {
252 FILE* fp = fopen(pel->second.path.c_str(), "rb");
253
254 if (fp == nullptr)
255 {
256 auto e = errno;
257 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
258 entry("PATH=%s", pel->second.path.c_str()));
259 throw file_error::Open();
260 }
261
262 // Must leave the file open here. It will be closed by sdbusplus
263 // when it sends it back over D-Bus.
264
265 return fileno(fp);
266 }
267 return std::nullopt;
268}
269
Matt Spinler1ea78802019-11-01 13:04:59 -0500270void Repository::for_each(ForEachFunc func) const
271{
Matt Spinler0ff00482019-11-06 16:19:46 -0600272 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500273 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600274 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500275
276 if (!file.good())
277 {
278 auto e = errno;
279 log<level::ERR>("Repository::for_each: Unable to open PEL file",
280 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600281 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500282 continue;
283 }
284
285 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
286 std::istreambuf_iterator<char>()};
287 file.close();
288
289 PEL pel{data};
290
291 try
292 {
293 if (func(pel))
294 {
295 break;
296 }
297 }
298 catch (std::exception& e)
299 {
300 log<level::ERR>("Repository::for_each function exception",
301 entry("ERROR=%s", e.what()));
302 }
303 }
304}
305
Matt Spinler421f6532019-11-06 15:40:45 -0600306void Repository::processAddCallbacks(const PEL& pel) const
307{
308 for (auto& [name, func] : _addSubscriptions)
309 {
310 try
311 {
312 func(pel);
313 }
314 catch (std::exception& e)
315 {
316 log<level::ERR>("PEL Repository add callback exception",
317 entry("NAME=%s", name.c_str()),
318 entry("ERROR=%s", e.what()));
319 }
320 }
321}
322
323void Repository::processDeleteCallbacks(uint32_t id) const
324{
325 for (auto& [name, func] : _deleteSubscriptions)
326 {
327 try
328 {
329 func(id);
330 }
331 catch (std::exception& e)
332 {
333 log<level::ERR>("PEL Repository delete callback exception",
334 entry("NAME=%s", name.c_str()),
335 entry("ERROR=%s", e.what()));
336 }
337 }
338}
339
Matt Spinler0ff00482019-11-06 16:19:46 -0600340std::optional<std::reference_wrapper<const Repository::PELAttributes>>
341 Repository::getPELAttributes(const LogID& id) const
342{
343 auto pel = findPEL(id);
344 if (pel != _pelAttributes.end())
345 {
346 return pel->second;
347 }
348
349 return std::nullopt;
350}
351
Matt Spinler29d18c12019-11-21 13:31:27 -0600352void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
353{
354 LogID id{LogID::Pel{pelID}};
355 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
356 [&id](const auto& a) { return a.first == id; });
357
358 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
359 {
360 PELUpdateFunc func = [state](PEL& pel) {
361 pel.setHostTransmissionState(state);
362 };
363
364 try
365 {
366 updatePEL(attr->second.path, func);
367
368 attr->second.hostState = state;
369 }
370 catch (std::exception& e)
371 {
372 log<level::ERR>("Unable to update PEL host transmission state",
373 entry("PATH=%s", attr->second.path.c_str()),
374 entry("ERROR=%s", e.what()));
375 }
376 }
377}
378
379void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
380{
381 LogID id{LogID::Pel{pelID}};
382 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
383 [&id](const auto& a) { return a.first == id; });
384
385 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
386 {
387 PELUpdateFunc func = [state](PEL& pel) {
388 pel.setHMCTransmissionState(state);
389 };
390
391 try
392 {
393 updatePEL(attr->second.path, func);
394
395 attr->second.hmcState = state;
396 }
397 catch (std::exception& e)
398 {
399 log<level::ERR>("Unable to update PEL HMC transmission state",
400 entry("PATH=%s", attr->second.path.c_str()),
401 entry("ERROR=%s", e.what()));
402 }
403 }
404}
405
406void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
407{
408 std::ifstream file{path};
409 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
410 std::istreambuf_iterator<char>()};
411 file.close();
412
413 PEL pel{data};
414
415 if (pel.valid())
416 {
417 updateFunc(pel);
418
419 write(pel, path);
420 }
421 else
422 {
423 throw std::runtime_error(
424 "Unable to read a valid PEL when trying to update it");
425 }
426}
427
Matt Spinler89fa0822019-07-17 13:54:30 -0500428} // namespace pels
429} // namespace openpower