blob: aaa6a870246a0e4aecfac36eab5f9f4d54ff5133 [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);
Matt Spinler421f6532019-11-06 15:40:45 -0600131
132 processAddCallbacks(*pel);
Matt Spinler475e5742019-07-18 16:09:49 -0500133}
134
135void Repository::remove(const LogID& id)
136{
137 auto pel = findPEL(id);
138 if (pel != _idsToPELs.end())
139 {
140 fs::remove(pel->second);
141 _idsToPELs.erase(pel);
Matt Spinler421f6532019-11-06 15:40:45 -0600142
143 processDeleteCallbacks(id.pelID.id);
Matt Spinler475e5742019-07-18 16:09:49 -0500144 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500145}
146
Matt Spinler2813f362019-07-19 12:45:28 -0500147std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
148{
149 auto pel = findPEL(id);
150 if (pel != _idsToPELs.end())
151 {
152 std::ifstream file{pel->second.c_str()};
153 if (!file.good())
154 {
155 auto e = errno;
156 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
157 entry("PATH=%s", pel->second.c_str()));
158 throw file_error::Open();
159 }
160
161 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
162 std::istreambuf_iterator<char>()};
163 return data;
164 }
165
166 return std::nullopt;
167}
168
Matt Spinler1ea78802019-11-01 13:04:59 -0500169void Repository::for_each(ForEachFunc func) const
170{
171 for (const auto& [id, path] : _idsToPELs)
172 {
173 std::ifstream file{path};
174
175 if (!file.good())
176 {
177 auto e = errno;
178 log<level::ERR>("Repository::for_each: Unable to open PEL file",
179 entry("ERRNO=%d", e),
180 entry("PATH=%s", path.c_str()));
181 continue;
182 }
183
184 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
185 std::istreambuf_iterator<char>()};
186 file.close();
187
188 PEL pel{data};
189
190 try
191 {
192 if (func(pel))
193 {
194 break;
195 }
196 }
197 catch (std::exception& e)
198 {
199 log<level::ERR>("Repository::for_each function exception",
200 entry("ERROR=%s", e.what()));
201 }
202 }
203}
204
Matt Spinler421f6532019-11-06 15:40:45 -0600205void Repository::processAddCallbacks(const PEL& pel) const
206{
207 for (auto& [name, func] : _addSubscriptions)
208 {
209 try
210 {
211 func(pel);
212 }
213 catch (std::exception& e)
214 {
215 log<level::ERR>("PEL Repository add callback exception",
216 entry("NAME=%s", name.c_str()),
217 entry("ERROR=%s", e.what()));
218 }
219 }
220}
221
222void Repository::processDeleteCallbacks(uint32_t id) const
223{
224 for (auto& [name, func] : _deleteSubscriptions)
225 {
226 try
227 {
228 func(id);
229 }
230 catch (std::exception& e)
231 {
232 log<level::ERR>("PEL Repository delete callback exception",
233 entry("NAME=%s", name.c_str()),
234 entry("ERROR=%s", e.what()));
235 }
236 }
237}
238
Matt Spinler89fa0822019-07-17 13:54:30 -0500239} // namespace pels
240} // namespace openpower