blob: fea2ba931b988e491d1547f72026403c37d0fd53 [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 Spinler4e8078c2019-07-09 13:22:32 -050016#include "manager.hpp"
17
18#include "additional_data.hpp"
Matt Spinler05c2c6c2019-12-18 14:02:09 -060019#include "json_utils.hpp"
Matt Spinler89fa0822019-07-17 13:54:30 -050020#include "pel.hpp"
21
Matt Spinler6b1a5c82020-01-07 08:48:53 -060022#include <unistd.h>
23
Matt Spinler89fa0822019-07-17 13:54:30 -050024#include <filesystem>
25#include <fstream>
Matt Spinlera34ab722019-12-16 10:39:32 -060026#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler56ad2a02020-03-26 14:00:52 -050027#include <xyz/openbmc_project/Logging/Create/server.hpp>
Matt Spinler4e8078c2019-07-09 13:22:32 -050028
29namespace openpower
30{
31namespace pels
32{
33
34using namespace phosphor::logging;
Matt Spinler89fa0822019-07-17 13:54:30 -050035namespace fs = std::filesystem;
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080036namespace rg = openpower::pels::message;
Matt Spinler4e8078c2019-07-09 13:22:32 -050037
Matt Spinlera34ab722019-12-16 10:39:32 -060038namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
39
Matt Spinler56ad2a02020-03-26 14:00:52 -050040using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create;
41
Matt Spinler4e8078c2019-07-09 13:22:32 -050042namespace additional_data
43{
44constexpr auto rawPEL = "RAWPEL";
Matt Spinler19e72902020-01-24 11:05:20 -060045constexpr auto esel = "ESEL";
46} // namespace additional_data
Matt Spinler4e8078c2019-07-09 13:22:32 -050047
48void Manager::create(const std::string& message, uint32_t obmcLogID,
49 uint64_t timestamp, Entry::Level severity,
50 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050051 const std::vector<std::string>& associations,
52 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -050053{
54 AdditionalData ad{additionalData};
55
Matt Spinler19e72902020-01-24 11:05:20 -060056 // If a PEL was passed in via a filename or in an ESEL,
57 // use that. Otherwise, create one.
Matt Spinler4e8078c2019-07-09 13:22:32 -050058 auto rawPelPath = ad.getValue(additional_data::rawPEL);
59 if (rawPelPath)
60 {
61 addRawPEL(*rawPelPath, obmcLogID);
62 }
63 else
64 {
Matt Spinler19e72902020-01-24 11:05:20 -060065 auto esel = ad.getValue(additional_data::esel);
66 if (esel)
67 {
68 addESELPEL(*esel, obmcLogID);
69 }
70 else
71 {
72 createPEL(message, obmcLogID, timestamp, severity, additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050073 associations, ffdc);
Matt Spinler19e72902020-01-24 11:05:20 -060074 }
Matt Spinler4e8078c2019-07-09 13:22:32 -050075 }
76}
77
78void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID)
79{
Matt Spinler89fa0822019-07-17 13:54:30 -050080 if (fs::exists(rawPelPath))
81 {
82 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary);
83
84 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file),
85 std::istreambuf_iterator<char>());
86 if (file.fail())
87 {
88 log<level::ERR>("Filesystem error reading a raw PEL",
89 entry("PELFILE=%s", rawPelPath.c_str()),
90 entry("OBMCLOGID=%d", obmcLogID));
91 // TODO, Decide what to do here. Maybe nothing.
92 return;
93 }
94
95 file.close();
96
Matt Spinler19e72902020-01-24 11:05:20 -060097 addPEL(data, obmcLogID);
Matt Spinler89fa0822019-07-17 13:54:30 -050098 }
99 else
100 {
101 log<level::ERR>("Raw PEL file from BMC event log does not exist",
102 entry("PELFILE=%s", (rawPelPath).c_str()),
103 entry("OBMCLOGID=%d", obmcLogID));
104 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500105}
106
Matt Spinler19e72902020-01-24 11:05:20 -0600107void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID)
108{
109
110 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID);
111 if (pel->valid())
112 {
113 // PELs created by others still need these fields set by us.
114 pel->assignID();
115 pel->setCommitTime();
116
117 try
118 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600119 log<level::DEBUG>("Adding external PEL to repo",
120 entry("PEL_ID=0x%X", pel->id()));
121
Matt Spinler19e72902020-01-24 11:05:20 -0600122 _repo.add(pel);
Matt Spinler7e727a32020-07-07 15:00:17 -0500123
124 if (_repo.sizeWarning())
125 {
126 scheduleRepoPrune();
127 }
Matt Spinler19e72902020-01-24 11:05:20 -0600128 }
129 catch (std::exception& e)
130 {
131 // Probably a full or r/o filesystem, not much we can do.
132 log<level::ERR>("Unable to add PEL to Repository",
133 entry("PEL_ID=0x%X", pel->id()));
134 }
135 }
136 else
137 {
138 log<level::ERR>("Invalid PEL received from the host",
139 entry("OBMCLOGID=%d", obmcLogID));
140
141 AdditionalData ad;
142 ad.add("PLID", getNumberString("0x%08X", pel->plid()));
143 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID));
144 ad.add("PEL_SIZE", std::to_string(pelData.size()));
145
146 std::string asciiString;
147 auto src = pel->primarySRC();
148 if (src)
149 {
150 asciiString = (*src)->asciiString();
151 }
152
153 ad.add("SRC", asciiString);
154
155 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL",
156 Entry::Level::Error, ad);
Matt Spinlerfe721892020-04-02 10:28:08 -0500157
158 // Save it to a file for debug in the lab. Just keep the latest.
159 // Not adding it to the PEL because it could already be max size
160 // and don't want to truncate an already invalid PEL.
161 std::ofstream pelFile{getPELRepoPath() / "badPEL"};
162 pelFile.write(reinterpret_cast<const char*>(pelData.data()),
163 pelData.size());
Matt Spinler19e72902020-01-24 11:05:20 -0600164 }
165}
166
167void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID)
168{
169 std::vector<uint8_t> data;
170
Matt Spinler5f5352e2020-03-05 16:23:27 -0600171 log<level::DEBUG>("Adding PEL from ESEL",
172 entry("OBMC_LOG_ID=%d", obmcLogID));
173
Matt Spinler19e72902020-01-24 11:05:20 -0600174 try
175 {
176 data = std::move(eselToRawData(esel));
177 }
178 catch (std::exception& e)
179 {
180 // Try to add it below anyway, so it follows the usual bad data path.
181 log<level::ERR>("Problems converting ESEL string to a byte vector");
182 }
183
184 addPEL(data, obmcLogID);
185}
186
187std::vector<uint8_t> Manager::eselToRawData(const std::string& esel)
188{
189 std::vector<uint8_t> data;
190 std::string byteString;
191
192 // As the eSEL string looks like: "50 48 00 ab ..." there are 3
193 // characters per raw byte, and since the actual PEL data starts
194 // at the 16th byte, the code will grab the PEL data starting at
195 // offset 48 in the string.
196 static constexpr size_t pelStart = 16 * 3;
197
198 if (esel.size() <= pelStart)
199 {
200 log<level::ERR>("ESEL data too short",
201 entry("ESEL_SIZE=%d", esel.size()));
202
203 throw std::length_error("ESEL data too short");
204 }
205
206 for (size_t i = pelStart; i < esel.size(); i += 3)
207 {
208 if (i + 1 < esel.size())
209 {
210 byteString = esel.substr(i, 2);
211 data.push_back(std::stoi(byteString, nullptr, 16));
212 }
213 else
214 {
215 log<level::ERR>("ESEL data too short",
216 entry("ESEL_SIZE=%d", esel.size()));
217 throw std::length_error("ESEL data too short");
218 }
219 }
220
221 return data;
222}
223
Matt Spinler4e8078c2019-07-09 13:22:32 -0500224void Manager::erase(uint32_t obmcLogID)
225{
Matt Spinler475e5742019-07-18 16:09:49 -0500226 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
227
228 _repo.remove(id);
Matt Spinler4e8078c2019-07-09 13:22:32 -0500229}
230
231bool Manager::isDeleteProhibited(uint32_t obmcLogID)
232{
233 return false;
234}
235
Matt Spinler56ad2a02020-03-26 14:00:52 -0500236PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc)
237{
238 PelFFDC pelFFDC;
239
240 std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) {
241 PelFFDCfile pf;
242 pf.subType = std::get<ffdcSubtypePos>(f);
243 pf.version = std::get<ffdcVersionPos>(f);
244 pf.fd = std::get<ffdcFDPos>(f);
245
246 switch (std::get<ffdcFormatPos>(f))
247 {
248 case Create::FFDCFormat::JSON:
249 pf.format = UserDataFormat::json;
250 break;
251 case Create::FFDCFormat::CBOR:
252 pf.format = UserDataFormat::cbor;
253 break;
254 case Create::FFDCFormat::Text:
255 pf.format = UserDataFormat::text;
256 break;
257 case Create::FFDCFormat::Custom:
258 pf.format = UserDataFormat::custom;
259 break;
260 }
261
262 pelFFDC.push_back(pf);
263 });
264
265 return pelFFDC;
266}
267
Matt Spinler4e8078c2019-07-09 13:22:32 -0500268void Manager::createPEL(const std::string& message, uint32_t obmcLogID,
269 uint64_t timestamp,
270 phosphor::logging::Entry::Level severity,
271 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -0500272 const std::vector<std::string>& associations,
273 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -0500274{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800275 auto entry = _registry.lookup(message, rg::LookupType::name);
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600276 std::string msg;
Matt Spinler67456c22019-10-21 12:22:49 -0500277
278 if (entry)
279 {
280 AdditionalData ad{additionalData};
281
Matt Spinler56ad2a02020-03-26 14:00:52 -0500282 auto pelFFDC = convertToPelFFDC(ffdc);
283
Matt Spinlera34ab722019-12-16 10:39:32 -0600284 auto pel = std::make_unique<openpower::pels::PEL>(
Matt Spinler56ad2a02020-03-26 14:00:52 -0500285 *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
Matt Spinler67456c22019-10-21 12:22:49 -0500286
287 _repo.add(pel);
Matt Spinler67456c22019-10-21 12:22:49 -0500288
Matt Spinler7e727a32020-07-07 15:00:17 -0500289 if (_repo.sizeWarning())
290 {
291 scheduleRepoPrune();
292 }
293
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600294 auto src = pel->primarySRC();
295 if (src)
296 {
297 using namespace std::literals::string_literals;
Matt Spinler19e72902020-01-24 11:05:20 -0600298 auto id = getNumberString("0x%08X", pel->id());
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600299 msg = "Created PEL "s + id + " with SRC "s + (*src)->asciiString();
300 while (msg.back() == ' ')
301 {
302 msg.pop_back();
303 }
304 log<level::INFO>(msg.c_str());
305 }
306 }
307 else
308 {
309 // TODO ibm-openbmc/dev/1151: Create a new PEL for this case.
310 // For now, just trace it.
311 msg = "Event not found in PEL message registry: " + message;
312 log<level::INFO>(msg.c_str());
313 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500314}
315
Matt Spinlera34ab722019-12-16 10:39:32 -0600316sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
317{
318 Repository::LogID id{Repository::LogID::Pel(pelID)};
319 std::optional<int> fd;
320
Matt Spinler5f5352e2020-03-05 16:23:27 -0600321 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID));
322
Matt Spinlera34ab722019-12-16 10:39:32 -0600323 try
324 {
325 fd = _repo.getPELFD(id);
326 }
327 catch (std::exception& e)
328 {
329 throw common_error::InternalFailure();
330 }
331
332 if (!fd)
333 {
334 throw common_error::InvalidArgument();
335 }
336
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600337 scheduleFDClose(*fd);
338
Matt Spinlera34ab722019-12-16 10:39:32 -0600339 return *fd;
340}
341
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600342void Manager::scheduleFDClose(int fd)
343{
Matt Spinler104e9362020-04-02 09:34:41 -0500344 sdeventplus::Event event = sdeventplus::Event::get_default();
345
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600346 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>(
Matt Spinler104e9362020-04-02 09:34:41 -0500347 event, std::bind(std::mem_fn(&Manager::closeFD), this, fd,
348 std::placeholders::_1));
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600349}
350
351void Manager::closeFD(int fd, sdeventplus::source::EventBase& source)
352{
353 close(fd);
354 _fdCloserEventSource.reset();
355}
356
Matt Spinlera34ab722019-12-16 10:39:32 -0600357std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID)
358{
359 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
360 std::optional<std::vector<uint8_t>> data;
361
Matt Spinler5f5352e2020-03-05 16:23:27 -0600362 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID));
363
Matt Spinlera34ab722019-12-16 10:39:32 -0600364 try
365 {
366 data = _repo.getPELData(id);
367 }
368 catch (std::exception& e)
369 {
370 throw common_error::InternalFailure();
371 }
372
373 if (!data)
374 {
375 throw common_error::InvalidArgument();
376 }
377
378 return *data;
379}
380
381void Manager::hostAck(uint32_t pelID)
382{
383 Repository::LogID id{Repository::LogID::Pel(pelID)};
384
Matt Spinler5f5352e2020-03-05 16:23:27 -0600385 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID));
386
Matt Spinlera34ab722019-12-16 10:39:32 -0600387 if (!_repo.hasPEL(id))
388 {
389 throw common_error::InvalidArgument();
390 }
391
392 if (_hostNotifier)
393 {
394 _hostNotifier->ackPEL(pelID);
395 }
396}
397
398void Manager::hostReject(uint32_t pelID, RejectionReason reason)
399{
400 Repository::LogID id{Repository::LogID::Pel(pelID)};
401
Matt Spinler5f5352e2020-03-05 16:23:27 -0600402 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID),
403 entry("REASON=%d", static_cast<int>(reason)));
404
Matt Spinlera34ab722019-12-16 10:39:32 -0600405 if (!_repo.hasPEL(id))
406 {
407 throw common_error::InvalidArgument();
408 }
409
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600410 if (reason == RejectionReason::BadPEL)
Matt Spinlera34ab722019-12-16 10:39:32 -0600411 {
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600412 AdditionalData data;
413 data.add("BAD_ID", getNumberString("0x%08X", pelID));
414 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost",
415 Entry::Level::Informational, data);
416 if (_hostNotifier)
Matt Spinlera34ab722019-12-16 10:39:32 -0600417 {
418 _hostNotifier->setBadPEL(pelID);
419 }
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600420 }
421 else if ((reason == RejectionReason::HostFull) && _hostNotifier)
422 {
423 _hostNotifier->setHostFull(pelID);
Matt Spinlera34ab722019-12-16 10:39:32 -0600424 }
425}
426
Matt Spinler7e727a32020-07-07 15:00:17 -0500427void Manager::scheduleRepoPrune()
428{
429 sdeventplus::Event event = sdeventplus::Event::get_default();
430
431 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>(
432 event, std::bind(std::mem_fn(&Manager::pruneRepo), this,
433 std::placeholders::_1));
434}
435
436void Manager::pruneRepo(sdeventplus::source::EventBase& source)
437{
438 auto idsToDelete = _repo.prune();
439
440 // Remove the OpenBMC event logs for the PELs that were just removed.
441 std::for_each(idsToDelete.begin(), idsToDelete.end(),
442 [this](auto id) { this->_logManager.erase(id); });
443
444 _repoPrunerEventSource.reset();
445}
446
Matt Spinler4e8078c2019-07-09 13:22:32 -0500447} // namespace pels
448} // namespace openpower