blob: 2de3ee9889abe0cd71451d5cbd5fbddfae72134f [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 Spinler5f5352e2020-03-05 16:23:27 -0600171 log<level::DEBUG>("Removing PEL from repository",
172 entry("PEL_ID=0x%X", pel->first.pelID.id),
173 entry("OBMC_LOG_ID=%d", pel->first.obmcID.id));
Matt Spinler0ff00482019-11-06 16:19:46 -0600174 fs::remove(pel->second.path);
175 _pelAttributes.erase(pel);
Matt Spinler421f6532019-11-06 15:40:45 -0600176
Matt Spinler5f5352e2020-03-05 16:23:27 -0600177 processDeleteCallbacks(pel->first.pelID.id);
178 }
179 else
180 {
181 log<level::DEBUG>("Could not find PEL to remove",
182 entry("PEL_ID=0x%X", id.pelID.id),
183 entry("OBMC_LOG_ID=%d", id.obmcID.id));
Matt Spinler475e5742019-07-18 16:09:49 -0500184 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500185}
186
Matt Spinler2813f362019-07-19 12:45:28 -0500187std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
188{
189 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600190 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500191 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600192 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500193 if (!file.good())
194 {
195 auto e = errno;
196 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600197 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500198 throw file_error::Open();
199 }
200
201 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
202 std::istreambuf_iterator<char>()};
203 return data;
204 }
205
206 return std::nullopt;
207}
208
Matt Spinler6d512242019-12-09 13:44:17 -0600209std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
210{
211 auto pel = findPEL(id);
212 if (pel != _pelAttributes.end())
213 {
214 FILE* fp = fopen(pel->second.path.c_str(), "rb");
215
216 if (fp == nullptr)
217 {
218 auto e = errno;
219 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
220 entry("PATH=%s", pel->second.path.c_str()));
221 throw file_error::Open();
222 }
223
224 // Must leave the file open here. It will be closed by sdbusplus
225 // when it sends it back over D-Bus.
226
227 return fileno(fp);
228 }
229 return std::nullopt;
230}
231
Matt Spinler1ea78802019-11-01 13:04:59 -0500232void Repository::for_each(ForEachFunc func) const
233{
Matt Spinler0ff00482019-11-06 16:19:46 -0600234 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500235 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600236 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500237
238 if (!file.good())
239 {
240 auto e = errno;
241 log<level::ERR>("Repository::for_each: Unable to open PEL file",
242 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600243 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500244 continue;
245 }
246
247 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
248 std::istreambuf_iterator<char>()};
249 file.close();
250
251 PEL pel{data};
252
253 try
254 {
255 if (func(pel))
256 {
257 break;
258 }
259 }
260 catch (std::exception& e)
261 {
262 log<level::ERR>("Repository::for_each function exception",
263 entry("ERROR=%s", e.what()));
264 }
265 }
266}
267
Matt Spinler421f6532019-11-06 15:40:45 -0600268void Repository::processAddCallbacks(const PEL& pel) const
269{
270 for (auto& [name, func] : _addSubscriptions)
271 {
272 try
273 {
274 func(pel);
275 }
276 catch (std::exception& e)
277 {
278 log<level::ERR>("PEL Repository add callback exception",
279 entry("NAME=%s", name.c_str()),
280 entry("ERROR=%s", e.what()));
281 }
282 }
283}
284
285void Repository::processDeleteCallbacks(uint32_t id) const
286{
287 for (auto& [name, func] : _deleteSubscriptions)
288 {
289 try
290 {
291 func(id);
292 }
293 catch (std::exception& e)
294 {
295 log<level::ERR>("PEL Repository delete callback exception",
296 entry("NAME=%s", name.c_str()),
297 entry("ERROR=%s", e.what()));
298 }
299 }
300}
301
Matt Spinler0ff00482019-11-06 16:19:46 -0600302std::optional<std::reference_wrapper<const Repository::PELAttributes>>
303 Repository::getPELAttributes(const LogID& id) const
304{
305 auto pel = findPEL(id);
306 if (pel != _pelAttributes.end())
307 {
308 return pel->second;
309 }
310
311 return std::nullopt;
312}
313
Matt Spinler29d18c12019-11-21 13:31:27 -0600314void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
315{
316 LogID id{LogID::Pel{pelID}};
317 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
318 [&id](const auto& a) { return a.first == id; });
319
320 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
321 {
322 PELUpdateFunc func = [state](PEL& pel) {
323 pel.setHostTransmissionState(state);
324 };
325
326 try
327 {
328 updatePEL(attr->second.path, func);
329
330 attr->second.hostState = state;
331 }
332 catch (std::exception& e)
333 {
334 log<level::ERR>("Unable to update PEL host transmission state",
335 entry("PATH=%s", attr->second.path.c_str()),
336 entry("ERROR=%s", e.what()));
337 }
338 }
339}
340
341void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
342{
343 LogID id{LogID::Pel{pelID}};
344 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
345 [&id](const auto& a) { return a.first == id; });
346
347 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
348 {
349 PELUpdateFunc func = [state](PEL& pel) {
350 pel.setHMCTransmissionState(state);
351 };
352
353 try
354 {
355 updatePEL(attr->second.path, func);
356
357 attr->second.hmcState = state;
358 }
359 catch (std::exception& e)
360 {
361 log<level::ERR>("Unable to update PEL HMC transmission state",
362 entry("PATH=%s", attr->second.path.c_str()),
363 entry("ERROR=%s", e.what()));
364 }
365 }
366}
367
368void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
369{
370 std::ifstream file{path};
371 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
372 std::istreambuf_iterator<char>()};
373 file.close();
374
375 PEL pel{data};
376
377 if (pel.valid())
378 {
379 updateFunc(pel);
380
381 write(pel, path);
382 }
383 else
384 {
385 throw std::runtime_error(
386 "Unable to read a valid PEL when trying to update it");
387 }
388}
389
Matt Spinler89fa0822019-07-17 13:54:30 -0500390} // namespace pels
391} // namespace openpower