blob: 3bf3cb029377594160bd750128c70f6ad3eba3fc [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 Spinler6d512242019-12-09 13:44:17 -0600200std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
201{
202 auto pel = findPEL(id);
203 if (pel != _pelAttributes.end())
204 {
205 FILE* fp = fopen(pel->second.path.c_str(), "rb");
206
207 if (fp == nullptr)
208 {
209 auto e = errno;
210 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
211 entry("PATH=%s", pel->second.path.c_str()));
212 throw file_error::Open();
213 }
214
215 // Must leave the file open here. It will be closed by sdbusplus
216 // when it sends it back over D-Bus.
217
218 return fileno(fp);
219 }
220 return std::nullopt;
221}
222
Matt Spinler1ea78802019-11-01 13:04:59 -0500223void Repository::for_each(ForEachFunc func) const
224{
Matt Spinler0ff00482019-11-06 16:19:46 -0600225 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500226 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600227 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500228
229 if (!file.good())
230 {
231 auto e = errno;
232 log<level::ERR>("Repository::for_each: Unable to open PEL file",
233 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600234 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500235 continue;
236 }
237
238 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
239 std::istreambuf_iterator<char>()};
240 file.close();
241
242 PEL pel{data};
243
244 try
245 {
246 if (func(pel))
247 {
248 break;
249 }
250 }
251 catch (std::exception& e)
252 {
253 log<level::ERR>("Repository::for_each function exception",
254 entry("ERROR=%s", e.what()));
255 }
256 }
257}
258
Matt Spinler421f6532019-11-06 15:40:45 -0600259void Repository::processAddCallbacks(const PEL& pel) const
260{
261 for (auto& [name, func] : _addSubscriptions)
262 {
263 try
264 {
265 func(pel);
266 }
267 catch (std::exception& e)
268 {
269 log<level::ERR>("PEL Repository add callback exception",
270 entry("NAME=%s", name.c_str()),
271 entry("ERROR=%s", e.what()));
272 }
273 }
274}
275
276void Repository::processDeleteCallbacks(uint32_t id) const
277{
278 for (auto& [name, func] : _deleteSubscriptions)
279 {
280 try
281 {
282 func(id);
283 }
284 catch (std::exception& e)
285 {
286 log<level::ERR>("PEL Repository delete callback exception",
287 entry("NAME=%s", name.c_str()),
288 entry("ERROR=%s", e.what()));
289 }
290 }
291}
292
Matt Spinler0ff00482019-11-06 16:19:46 -0600293std::optional<std::reference_wrapper<const Repository::PELAttributes>>
294 Repository::getPELAttributes(const LogID& id) const
295{
296 auto pel = findPEL(id);
297 if (pel != _pelAttributes.end())
298 {
299 return pel->second;
300 }
301
302 return std::nullopt;
303}
304
Matt Spinler29d18c12019-11-21 13:31:27 -0600305void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
306{
307 LogID id{LogID::Pel{pelID}};
308 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
309 [&id](const auto& a) { return a.first == id; });
310
311 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
312 {
313 PELUpdateFunc func = [state](PEL& pel) {
314 pel.setHostTransmissionState(state);
315 };
316
317 try
318 {
319 updatePEL(attr->second.path, func);
320
321 attr->second.hostState = state;
322 }
323 catch (std::exception& e)
324 {
325 log<level::ERR>("Unable to update PEL host transmission state",
326 entry("PATH=%s", attr->second.path.c_str()),
327 entry("ERROR=%s", e.what()));
328 }
329 }
330}
331
332void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
333{
334 LogID id{LogID::Pel{pelID}};
335 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
336 [&id](const auto& a) { return a.first == id; });
337
338 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
339 {
340 PELUpdateFunc func = [state](PEL& pel) {
341 pel.setHMCTransmissionState(state);
342 };
343
344 try
345 {
346 updatePEL(attr->second.path, func);
347
348 attr->second.hmcState = state;
349 }
350 catch (std::exception& e)
351 {
352 log<level::ERR>("Unable to update PEL HMC transmission state",
353 entry("PATH=%s", attr->second.path.c_str()),
354 entry("ERROR=%s", e.what()));
355 }
356 }
357}
358
359void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
360{
361 std::ifstream file{path};
362 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
363 std::istreambuf_iterator<char>()};
364 file.close();
365
366 PEL pel{data};
367
368 if (pel.valid())
369 {
370 updateFunc(pel);
371
372 write(pel, path);
373 }
374 else
375 {
376 throw std::runtime_error(
377 "Unable to read a valid PEL when trying to update it");
378 }
379}
380
Matt Spinler89fa0822019-07-17 13:54:30 -0500381} // namespace pels
382} // namespace openpower