blob: e41e5b85955bb72e87909455669ed69456f76421 [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 Spinler22421b92020-07-17 09:41:08 -050022#include <fmt/format.h>
Matt Spinlerff9cec22020-07-15 13:06:35 -050023#include <sys/inotify.h>
Matt Spinler6b1a5c82020-01-07 08:48:53 -060024#include <unistd.h>
25
Matt Spinler89fa0822019-07-17 13:54:30 -050026#include <filesystem>
27#include <fstream>
Matt Spinlera34ab722019-12-16 10:39:32 -060028#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler56ad2a02020-03-26 14:00:52 -050029#include <xyz/openbmc_project/Logging/Create/server.hpp>
Matt Spinler4e8078c2019-07-09 13:22:32 -050030
31namespace openpower
32{
33namespace pels
34{
35
36using namespace phosphor::logging;
Matt Spinler89fa0822019-07-17 13:54:30 -050037namespace fs = std::filesystem;
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080038namespace rg = openpower::pels::message;
Matt Spinler4e8078c2019-07-09 13:22:32 -050039
Matt Spinlera34ab722019-12-16 10:39:32 -060040namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
41
Matt Spinler56ad2a02020-03-26 14:00:52 -050042using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create;
43
Matt Spinler4e8078c2019-07-09 13:22:32 -050044namespace additional_data
45{
46constexpr auto rawPEL = "RAWPEL";
Matt Spinler19e72902020-01-24 11:05:20 -060047constexpr auto esel = "ESEL";
Matt Spinler30ddc9f2020-07-16 15:39:59 -050048constexpr auto error = "ERROR_NAME";
Matt Spinler19e72902020-01-24 11:05:20 -060049} // namespace additional_data
Matt Spinler4e8078c2019-07-09 13:22:32 -050050
Matt Spinler30ddc9f2020-07-16 15:39:59 -050051constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default";
52
Matt Spinlerff9cec22020-07-15 13:06:35 -050053Manager::~Manager()
54{
55 if (_pelFileDeleteFD != -1)
56 {
57 if (_pelFileDeleteWatchFD != -1)
58 {
59 inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD);
60 }
61 close(_pelFileDeleteFD);
62 }
63}
64
Matt Spinler4e8078c2019-07-09 13:22:32 -050065void Manager::create(const std::string& message, uint32_t obmcLogID,
66 uint64_t timestamp, Entry::Level severity,
67 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050068 const std::vector<std::string>& associations,
69 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -050070{
71 AdditionalData ad{additionalData};
72
Matt Spinler19e72902020-01-24 11:05:20 -060073 // If a PEL was passed in via a filename or in an ESEL,
74 // use that. Otherwise, create one.
Matt Spinler4e8078c2019-07-09 13:22:32 -050075 auto rawPelPath = ad.getValue(additional_data::rawPEL);
76 if (rawPelPath)
77 {
78 addRawPEL(*rawPelPath, obmcLogID);
79 }
80 else
81 {
Matt Spinler19e72902020-01-24 11:05:20 -060082 auto esel = ad.getValue(additional_data::esel);
83 if (esel)
84 {
85 addESELPEL(*esel, obmcLogID);
86 }
87 else
88 {
89 createPEL(message, obmcLogID, timestamp, severity, additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050090 associations, ffdc);
Matt Spinler19e72902020-01-24 11:05:20 -060091 }
Matt Spinler4e8078c2019-07-09 13:22:32 -050092 }
93}
94
95void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID)
96{
Matt Spinler89fa0822019-07-17 13:54:30 -050097 if (fs::exists(rawPelPath))
98 {
99 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary);
100
101 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file),
102 std::istreambuf_iterator<char>());
103 if (file.fail())
104 {
105 log<level::ERR>("Filesystem error reading a raw PEL",
106 entry("PELFILE=%s", rawPelPath.c_str()),
107 entry("OBMCLOGID=%d", obmcLogID));
108 // TODO, Decide what to do here. Maybe nothing.
109 return;
110 }
111
112 file.close();
113
Matt Spinler19e72902020-01-24 11:05:20 -0600114 addPEL(data, obmcLogID);
Matt Spinler89fa0822019-07-17 13:54:30 -0500115 }
116 else
117 {
118 log<level::ERR>("Raw PEL file from BMC event log does not exist",
119 entry("PELFILE=%s", (rawPelPath).c_str()),
120 entry("OBMCLOGID=%d", obmcLogID));
121 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500122}
123
Matt Spinler19e72902020-01-24 11:05:20 -0600124void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID)
125{
126
127 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID);
128 if (pel->valid())
129 {
130 // PELs created by others still need these fields set by us.
131 pel->assignID();
132 pel->setCommitTime();
133
134 try
135 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600136 log<level::DEBUG>("Adding external PEL to repo",
137 entry("PEL_ID=0x%X", pel->id()));
138
Matt Spinler19e72902020-01-24 11:05:20 -0600139 _repo.add(pel);
Matt Spinler7e727a32020-07-07 15:00:17 -0500140
141 if (_repo.sizeWarning())
142 {
143 scheduleRepoPrune();
144 }
Matt Spinler19e72902020-01-24 11:05:20 -0600145 }
146 catch (std::exception& e)
147 {
148 // Probably a full or r/o filesystem, not much we can do.
149 log<level::ERR>("Unable to add PEL to Repository",
150 entry("PEL_ID=0x%X", pel->id()));
151 }
152 }
153 else
154 {
155 log<level::ERR>("Invalid PEL received from the host",
156 entry("OBMCLOGID=%d", obmcLogID));
157
158 AdditionalData ad;
159 ad.add("PLID", getNumberString("0x%08X", pel->plid()));
160 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID));
161 ad.add("PEL_SIZE", std::to_string(pelData.size()));
162
163 std::string asciiString;
164 auto src = pel->primarySRC();
165 if (src)
166 {
167 asciiString = (*src)->asciiString();
168 }
169
170 ad.add("SRC", asciiString);
171
172 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL",
173 Entry::Level::Error, ad);
Matt Spinlerfe721892020-04-02 10:28:08 -0500174
175 // Save it to a file for debug in the lab. Just keep the latest.
176 // Not adding it to the PEL because it could already be max size
177 // and don't want to truncate an already invalid PEL.
178 std::ofstream pelFile{getPELRepoPath() / "badPEL"};
179 pelFile.write(reinterpret_cast<const char*>(pelData.data()),
180 pelData.size());
Matt Spinler19e72902020-01-24 11:05:20 -0600181 }
182}
183
184void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID)
185{
186 std::vector<uint8_t> data;
187
Matt Spinler5f5352e2020-03-05 16:23:27 -0600188 log<level::DEBUG>("Adding PEL from ESEL",
189 entry("OBMC_LOG_ID=%d", obmcLogID));
190
Matt Spinler19e72902020-01-24 11:05:20 -0600191 try
192 {
193 data = std::move(eselToRawData(esel));
194 }
195 catch (std::exception& e)
196 {
197 // Try to add it below anyway, so it follows the usual bad data path.
198 log<level::ERR>("Problems converting ESEL string to a byte vector");
199 }
200
201 addPEL(data, obmcLogID);
202}
203
204std::vector<uint8_t> Manager::eselToRawData(const std::string& esel)
205{
206 std::vector<uint8_t> data;
207 std::string byteString;
208
209 // As the eSEL string looks like: "50 48 00 ab ..." there are 3
210 // characters per raw byte, and since the actual PEL data starts
211 // at the 16th byte, the code will grab the PEL data starting at
212 // offset 48 in the string.
213 static constexpr size_t pelStart = 16 * 3;
214
215 if (esel.size() <= pelStart)
216 {
217 log<level::ERR>("ESEL data too short",
218 entry("ESEL_SIZE=%d", esel.size()));
219
220 throw std::length_error("ESEL data too short");
221 }
222
223 for (size_t i = pelStart; i < esel.size(); i += 3)
224 {
225 if (i + 1 < esel.size())
226 {
227 byteString = esel.substr(i, 2);
228 data.push_back(std::stoi(byteString, nullptr, 16));
229 }
230 else
231 {
232 log<level::ERR>("ESEL data too short",
233 entry("ESEL_SIZE=%d", esel.size()));
234 throw std::length_error("ESEL data too short");
235 }
236 }
237
238 return data;
239}
240
Matt Spinler4e8078c2019-07-09 13:22:32 -0500241void Manager::erase(uint32_t obmcLogID)
242{
Matt Spinler475e5742019-07-18 16:09:49 -0500243 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
244
245 _repo.remove(id);
Matt Spinler4e8078c2019-07-09 13:22:32 -0500246}
247
248bool Manager::isDeleteProhibited(uint32_t obmcLogID)
249{
250 return false;
251}
252
Matt Spinler56ad2a02020-03-26 14:00:52 -0500253PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc)
254{
255 PelFFDC pelFFDC;
256
257 std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) {
258 PelFFDCfile pf;
259 pf.subType = std::get<ffdcSubtypePos>(f);
260 pf.version = std::get<ffdcVersionPos>(f);
261 pf.fd = std::get<ffdcFDPos>(f);
262
263 switch (std::get<ffdcFormatPos>(f))
264 {
265 case Create::FFDCFormat::JSON:
266 pf.format = UserDataFormat::json;
267 break;
268 case Create::FFDCFormat::CBOR:
269 pf.format = UserDataFormat::cbor;
270 break;
271 case Create::FFDCFormat::Text:
272 pf.format = UserDataFormat::text;
273 break;
274 case Create::FFDCFormat::Custom:
275 pf.format = UserDataFormat::custom;
276 break;
277 }
278
279 pelFFDC.push_back(pf);
280 });
281
282 return pelFFDC;
283}
284
Matt Spinler4e8078c2019-07-09 13:22:32 -0500285void Manager::createPEL(const std::string& message, uint32_t obmcLogID,
286 uint64_t timestamp,
287 phosphor::logging::Entry::Level severity,
288 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -0500289 const std::vector<std::string>& associations,
290 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -0500291{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800292 auto entry = _registry.lookup(message, rg::LookupType::name);
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500293 auto pelFFDC = convertToPelFFDC(ffdc);
294 AdditionalData ad{additionalData};
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600295 std::string msg;
Matt Spinler67456c22019-10-21 12:22:49 -0500296
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500297 if (!entry)
Matt Spinler67456c22019-10-21 12:22:49 -0500298 {
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500299 // Instead, get the default entry that means there is no
300 // other matching entry. This error will still use the
301 // AdditionalData values of the original error, and this
302 // code will add the error message value that wasn't found
303 // to this AD. This way, there will at least be a PEL,
304 // possibly with callouts, to allow users to debug the
305 // issue that caused the error even without its own PEL.
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600306 msg = "Event not found in PEL message registry: " + message;
307 log<level::INFO>(msg.c_str());
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500308
309 entry = _registry.lookup(defaultLogMessage, rg::LookupType::name);
310 if (!entry)
311 {
312 log<level::ERR>("Default event not found in PEL message registry");
313 return;
314 }
315
316 ad.add(additional_data::error, message);
317 }
318
319 auto pel = std::make_unique<openpower::pels::PEL>(
320 *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
321
322 _repo.add(pel);
323
324 if (_repo.sizeWarning())
325 {
326 scheduleRepoPrune();
327 }
328
329 auto src = pel->primarySRC();
330 if (src)
331 {
Matt Spinler22421b92020-07-17 09:41:08 -0500332 auto msg =
333 fmt::format("Created PEL {:#x} (BMC ID {}) with SRC {}", pel->id(),
334 pel->obmcLogID(), (*src)->asciiString());
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500335 while (msg.back() == ' ')
336 {
337 msg.pop_back();
338 }
339 log<level::INFO>(msg.c_str());
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600340 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500341}
342
Matt Spinlera34ab722019-12-16 10:39:32 -0600343sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
344{
345 Repository::LogID id{Repository::LogID::Pel(pelID)};
346 std::optional<int> fd;
347
Matt Spinler5f5352e2020-03-05 16:23:27 -0600348 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID));
349
Matt Spinlera34ab722019-12-16 10:39:32 -0600350 try
351 {
352 fd = _repo.getPELFD(id);
353 }
354 catch (std::exception& e)
355 {
356 throw common_error::InternalFailure();
357 }
358
359 if (!fd)
360 {
361 throw common_error::InvalidArgument();
362 }
363
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600364 scheduleFDClose(*fd);
365
Matt Spinlera34ab722019-12-16 10:39:32 -0600366 return *fd;
367}
368
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600369void Manager::scheduleFDClose(int fd)
370{
371 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>(
Matt Spinlerff9cec22020-07-15 13:06:35 -0500372 _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd,
373 std::placeholders::_1));
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600374}
375
376void Manager::closeFD(int fd, sdeventplus::source::EventBase& source)
377{
378 close(fd);
379 _fdCloserEventSource.reset();
380}
381
Matt Spinlera34ab722019-12-16 10:39:32 -0600382std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID)
383{
384 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
385 std::optional<std::vector<uint8_t>> data;
386
Matt Spinler5f5352e2020-03-05 16:23:27 -0600387 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID));
388
Matt Spinlera34ab722019-12-16 10:39:32 -0600389 try
390 {
391 data = _repo.getPELData(id);
392 }
393 catch (std::exception& e)
394 {
395 throw common_error::InternalFailure();
396 }
397
398 if (!data)
399 {
400 throw common_error::InvalidArgument();
401 }
402
403 return *data;
404}
405
406void Manager::hostAck(uint32_t pelID)
407{
408 Repository::LogID id{Repository::LogID::Pel(pelID)};
409
Matt Spinler5f5352e2020-03-05 16:23:27 -0600410 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID));
411
Matt Spinlera34ab722019-12-16 10:39:32 -0600412 if (!_repo.hasPEL(id))
413 {
414 throw common_error::InvalidArgument();
415 }
416
417 if (_hostNotifier)
418 {
419 _hostNotifier->ackPEL(pelID);
420 }
421}
422
423void Manager::hostReject(uint32_t pelID, RejectionReason reason)
424{
425 Repository::LogID id{Repository::LogID::Pel(pelID)};
426
Matt Spinler5f5352e2020-03-05 16:23:27 -0600427 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID),
428 entry("REASON=%d", static_cast<int>(reason)));
429
Matt Spinlera34ab722019-12-16 10:39:32 -0600430 if (!_repo.hasPEL(id))
431 {
432 throw common_error::InvalidArgument();
433 }
434
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600435 if (reason == RejectionReason::BadPEL)
Matt Spinlera34ab722019-12-16 10:39:32 -0600436 {
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600437 AdditionalData data;
438 data.add("BAD_ID", getNumberString("0x%08X", pelID));
439 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost",
440 Entry::Level::Informational, data);
441 if (_hostNotifier)
Matt Spinlera34ab722019-12-16 10:39:32 -0600442 {
443 _hostNotifier->setBadPEL(pelID);
444 }
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600445 }
446 else if ((reason == RejectionReason::HostFull) && _hostNotifier)
447 {
448 _hostNotifier->setHostFull(pelID);
Matt Spinlera34ab722019-12-16 10:39:32 -0600449 }
450}
451
Matt Spinler7e727a32020-07-07 15:00:17 -0500452void Manager::scheduleRepoPrune()
453{
Matt Spinler7e727a32020-07-07 15:00:17 -0500454 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>(
Matt Spinlerff9cec22020-07-15 13:06:35 -0500455 _event, std::bind(std::mem_fn(&Manager::pruneRepo), this,
456 std::placeholders::_1));
Matt Spinler7e727a32020-07-07 15:00:17 -0500457}
458
459void Manager::pruneRepo(sdeventplus::source::EventBase& source)
460{
461 auto idsToDelete = _repo.prune();
462
463 // Remove the OpenBMC event logs for the PELs that were just removed.
464 std::for_each(idsToDelete.begin(), idsToDelete.end(),
465 [this](auto id) { this->_logManager.erase(id); });
466
467 _repoPrunerEventSource.reset();
468}
469
Matt Spinlerff9cec22020-07-15 13:06:35 -0500470void Manager::setupPELDeleteWatch()
471{
472 _pelFileDeleteFD = inotify_init1(IN_NONBLOCK);
473 if (-1 == _pelFileDeleteFD)
474 {
475 auto e = errno;
476 std::string msg =
477 "inotify_init1 failed with errno " + std::to_string(e);
478 log<level::ERR>(msg.c_str());
479 abort();
480 }
481
482 _pelFileDeleteWatchFD = inotify_add_watch(
483 _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE);
484 if (-1 == _pelFileDeleteWatchFD)
485 {
486 auto e = errno;
487 std::string msg =
488 "inotify_add_watch failed with error " + std::to_string(e);
489 log<level::ERR>(msg.c_str());
490 abort();
491 }
492
493 _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>(
494 _event, _pelFileDeleteFD, EPOLLIN,
495 std::bind(std::mem_fn(&Manager::pelFileDeleted), this,
496 std::placeholders::_1, std::placeholders::_2,
497 std::placeholders::_3));
498}
499
500void Manager::pelFileDeleted(sdeventplus::source::IO& io, int fd,
501 uint32_t revents)
502{
503 if (!(revents & EPOLLIN))
504 {
505 return;
506 }
507
508 // An event for 1 PEL uses 48B. When all PELs are deleted at once,
509 // as many events as there is room for can be handled in one callback.
510 // A size of 2000 will allow 41 to be processed, with additional
511 // callbacks being needed to process the remaining ones.
512 std::array<uint8_t, 2000> data;
513 auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size());
514 if (bytesRead < 0)
515 {
516 auto e = errno;
517 std::string msg = "Failed reading data from inotify event, errno = " +
518 std::to_string(e);
519 log<level::ERR>(msg.c_str());
520 abort();
521 }
522
523 auto offset = 0;
524 while (offset < bytesRead)
525 {
526 auto event = reinterpret_cast<inotify_event*>(&data[offset]);
527 if (event->mask & IN_DELETE)
528 {
529 std::string filename{event->name};
530
531 // Get the PEL ID from the filename and tell the
532 // repo it's been removed, and then delete the BMC
533 // event log if it's there.
534 auto pos = filename.find_first_of('_');
535 if (pos != std::string::npos)
536 {
537 try
538 {
539 auto idString = filename.substr(pos + 1);
540 auto pelID = std::stoul(idString, nullptr, 16);
541
542 Repository::LogID id{Repository::LogID::Pel(pelID)};
543 auto removedLogID = _repo.remove(id);
544 if (removedLogID)
545 {
546 _logManager.erase(removedLogID->obmcID.id);
547 }
548 }
549 catch (const std::exception& e)
550 {
551 log<level::INFO>("Could not find PEL ID from its filename",
552 entry("FILENAME=%s", filename.c_str()));
553 }
554 }
555 }
556
557 offset += offsetof(inotify_event, name) + event->len;
558 }
559}
Matt Spinler4e8078c2019-07-09 13:22:32 -0500560} // namespace pels
561} // namespace openpower