blob: 21b02438ec43ad72d1a4758edd43a6b3ffa47db5 [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 {
61 using pelID = LogID::Pel;
62 using obmcID = LogID::Obmc;
63 _idsToPELs.emplace(
64 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
65 dirEntry.path());
66 }
67 else
68 {
69 log<level::ERR>(
70 "Found invalid PEL file while restoring. Removing.",
71 entry("FILENAME=%s", dirEntry.path().c_str()));
72 fs::remove(dirEntry.path());
73 }
74 }
75 catch (std::exception& e)
76 {
77 log<level::ERR>("Hit exception while restoring PEL File",
78 entry("FILENAME=%s", dirEntry.path().c_str()),
79 entry("ERROR=%s", e.what()));
80 }
81 }
Matt Spinler89fa0822019-07-17 13:54:30 -050082}
83
84std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
85{
86 char name[50];
87 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
88 time.yearLSB, time.month, time.day, time.hour, time.minutes,
89 time.seconds, time.hundredths, pelID);
90 return std::string{name};
91}
92
93void Repository::add(std::unique_ptr<PEL>& pel)
94{
95 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
96 std::ofstream file{path, std::ios::binary};
97
98 if (!file.good())
99 {
100 // If this fails, the filesystem is probably full so it isn't like
101 // we could successfully create yet another error log here.
102 auto e = errno;
103 log<level::ERR>("Failed creating PEL file",
104 entry("FILE=%s", path.c_str()));
105 fs::remove(path);
106 log<level::ERR>("Unable to open PEL file for writing",
107 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
108 throw file_error::Open();
109 }
110
111 auto data = pel->data();
112 file.write(reinterpret_cast<const char*>(data.data()), data.size());
113
114 if (file.fail())
115 {
116 // Same note as above about not being able to create an error log
117 // for this case even if we wanted.
118 auto e = errno;
119 log<level::ERR>("Failed writing PEL file",
120 entry("FILE=%s", path.c_str()));
121 file.close();
122 fs::remove(path);
123 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
124 entry("PATH=%s", path.c_str()));
125 throw file_error::Write();
126 }
Matt Spinler475e5742019-07-18 16:09:49 -0500127
128 using pelID = LogID::Pel;
129 using obmcID = LogID::Obmc;
130 _idsToPELs.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), path);
131}
132
133void Repository::remove(const LogID& id)
134{
135 auto pel = findPEL(id);
136 if (pel != _idsToPELs.end())
137 {
138 fs::remove(pel->second);
139 _idsToPELs.erase(pel);
140 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500141}
142
Matt Spinler2813f362019-07-19 12:45:28 -0500143std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
144{
145 auto pel = findPEL(id);
146 if (pel != _idsToPELs.end())
147 {
148 std::ifstream file{pel->second.c_str()};
149 if (!file.good())
150 {
151 auto e = errno;
152 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
153 entry("PATH=%s", pel->second.c_str()));
154 throw file_error::Open();
155 }
156
157 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
158 std::istreambuf_iterator<char>()};
159 return data;
160 }
161
162 return std::nullopt;
163}
164
Matt Spinler1ea78802019-11-01 13:04:59 -0500165void Repository::for_each(ForEachFunc func) const
166{
167 for (const auto& [id, path] : _idsToPELs)
168 {
169 std::ifstream file{path};
170
171 if (!file.good())
172 {
173 auto e = errno;
174 log<level::ERR>("Repository::for_each: Unable to open PEL file",
175 entry("ERRNO=%d", e),
176 entry("PATH=%s", path.c_str()));
177 continue;
178 }
179
180 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
181 std::istreambuf_iterator<char>()};
182 file.close();
183
184 PEL pel{data};
185
186 try
187 {
188 if (func(pel))
189 {
190 break;
191 }
192 }
193 catch (std::exception& e)
194 {
195 log<level::ERR>("Repository::for_each function exception",
196 entry("ERROR=%s", e.what()));
197 }
198 }
199}
200
Matt Spinler89fa0822019-07-17 13:54:30 -0500201} // namespace pels
202} // namespace openpower