blob: d7fbcad144a62441144a4c51db61e4e67da72f95 [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 Spinlerff9cec22020-07-15 13:06:35 -050022#include <sys/inotify.h>
Matt Spinler6b1a5c82020-01-07 08:48:53 -060023#include <unistd.h>
24
Matt Spinler89fa0822019-07-17 13:54:30 -050025#include <filesystem>
26#include <fstream>
Matt Spinlera34ab722019-12-16 10:39:32 -060027#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler56ad2a02020-03-26 14:00:52 -050028#include <xyz/openbmc_project/Logging/Create/server.hpp>
Matt Spinler4e8078c2019-07-09 13:22:32 -050029
30namespace openpower
31{
32namespace pels
33{
34
35using namespace phosphor::logging;
Matt Spinler89fa0822019-07-17 13:54:30 -050036namespace fs = std::filesystem;
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080037namespace rg = openpower::pels::message;
Matt Spinler4e8078c2019-07-09 13:22:32 -050038
Matt Spinlera34ab722019-12-16 10:39:32 -060039namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
40
Matt Spinler56ad2a02020-03-26 14:00:52 -050041using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create;
42
Matt Spinler4e8078c2019-07-09 13:22:32 -050043namespace additional_data
44{
45constexpr auto rawPEL = "RAWPEL";
Matt Spinler19e72902020-01-24 11:05:20 -060046constexpr auto esel = "ESEL";
47} // namespace additional_data
Matt Spinler4e8078c2019-07-09 13:22:32 -050048
Matt Spinlerff9cec22020-07-15 13:06:35 -050049Manager::~Manager()
50{
51 if (_pelFileDeleteFD != -1)
52 {
53 if (_pelFileDeleteWatchFD != -1)
54 {
55 inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD);
56 }
57 close(_pelFileDeleteFD);
58 }
59}
60
Matt Spinler4e8078c2019-07-09 13:22:32 -050061void Manager::create(const std::string& message, uint32_t obmcLogID,
62 uint64_t timestamp, Entry::Level severity,
63 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050064 const std::vector<std::string>& associations,
65 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -050066{
67 AdditionalData ad{additionalData};
68
Matt Spinler19e72902020-01-24 11:05:20 -060069 // If a PEL was passed in via a filename or in an ESEL,
70 // use that. Otherwise, create one.
Matt Spinler4e8078c2019-07-09 13:22:32 -050071 auto rawPelPath = ad.getValue(additional_data::rawPEL);
72 if (rawPelPath)
73 {
74 addRawPEL(*rawPelPath, obmcLogID);
75 }
76 else
77 {
Matt Spinler19e72902020-01-24 11:05:20 -060078 auto esel = ad.getValue(additional_data::esel);
79 if (esel)
80 {
81 addESELPEL(*esel, obmcLogID);
82 }
83 else
84 {
85 createPEL(message, obmcLogID, timestamp, severity, additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -050086 associations, ffdc);
Matt Spinler19e72902020-01-24 11:05:20 -060087 }
Matt Spinler4e8078c2019-07-09 13:22:32 -050088 }
89}
90
91void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID)
92{
Matt Spinler89fa0822019-07-17 13:54:30 -050093 if (fs::exists(rawPelPath))
94 {
95 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary);
96
97 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file),
98 std::istreambuf_iterator<char>());
99 if (file.fail())
100 {
101 log<level::ERR>("Filesystem error reading a raw PEL",
102 entry("PELFILE=%s", rawPelPath.c_str()),
103 entry("OBMCLOGID=%d", obmcLogID));
104 // TODO, Decide what to do here. Maybe nothing.
105 return;
106 }
107
108 file.close();
109
Matt Spinler19e72902020-01-24 11:05:20 -0600110 addPEL(data, obmcLogID);
Matt Spinler89fa0822019-07-17 13:54:30 -0500111 }
112 else
113 {
114 log<level::ERR>("Raw PEL file from BMC event log does not exist",
115 entry("PELFILE=%s", (rawPelPath).c_str()),
116 entry("OBMCLOGID=%d", obmcLogID));
117 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500118}
119
Matt Spinler19e72902020-01-24 11:05:20 -0600120void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID)
121{
122
123 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID);
124 if (pel->valid())
125 {
126 // PELs created by others still need these fields set by us.
127 pel->assignID();
128 pel->setCommitTime();
129
130 try
131 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600132 log<level::DEBUG>("Adding external PEL to repo",
133 entry("PEL_ID=0x%X", pel->id()));
134
Matt Spinler19e72902020-01-24 11:05:20 -0600135 _repo.add(pel);
Matt Spinler7e727a32020-07-07 15:00:17 -0500136
137 if (_repo.sizeWarning())
138 {
139 scheduleRepoPrune();
140 }
Matt Spinler19e72902020-01-24 11:05:20 -0600141 }
142 catch (std::exception& e)
143 {
144 // Probably a full or r/o filesystem, not much we can do.
145 log<level::ERR>("Unable to add PEL to Repository",
146 entry("PEL_ID=0x%X", pel->id()));
147 }
148 }
149 else
150 {
151 log<level::ERR>("Invalid PEL received from the host",
152 entry("OBMCLOGID=%d", obmcLogID));
153
154 AdditionalData ad;
155 ad.add("PLID", getNumberString("0x%08X", pel->plid()));
156 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID));
157 ad.add("PEL_SIZE", std::to_string(pelData.size()));
158
159 std::string asciiString;
160 auto src = pel->primarySRC();
161 if (src)
162 {
163 asciiString = (*src)->asciiString();
164 }
165
166 ad.add("SRC", asciiString);
167
168 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL",
169 Entry::Level::Error, ad);
Matt Spinlerfe721892020-04-02 10:28:08 -0500170
171 // Save it to a file for debug in the lab. Just keep the latest.
172 // Not adding it to the PEL because it could already be max size
173 // and don't want to truncate an already invalid PEL.
174 std::ofstream pelFile{getPELRepoPath() / "badPEL"};
175 pelFile.write(reinterpret_cast<const char*>(pelData.data()),
176 pelData.size());
Matt Spinler19e72902020-01-24 11:05:20 -0600177 }
178}
179
180void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID)
181{
182 std::vector<uint8_t> data;
183
Matt Spinler5f5352e2020-03-05 16:23:27 -0600184 log<level::DEBUG>("Adding PEL from ESEL",
185 entry("OBMC_LOG_ID=%d", obmcLogID));
186
Matt Spinler19e72902020-01-24 11:05:20 -0600187 try
188 {
189 data = std::move(eselToRawData(esel));
190 }
191 catch (std::exception& e)
192 {
193 // Try to add it below anyway, so it follows the usual bad data path.
194 log<level::ERR>("Problems converting ESEL string to a byte vector");
195 }
196
197 addPEL(data, obmcLogID);
198}
199
200std::vector<uint8_t> Manager::eselToRawData(const std::string& esel)
201{
202 std::vector<uint8_t> data;
203 std::string byteString;
204
205 // As the eSEL string looks like: "50 48 00 ab ..." there are 3
206 // characters per raw byte, and since the actual PEL data starts
207 // at the 16th byte, the code will grab the PEL data starting at
208 // offset 48 in the string.
209 static constexpr size_t pelStart = 16 * 3;
210
211 if (esel.size() <= pelStart)
212 {
213 log<level::ERR>("ESEL data too short",
214 entry("ESEL_SIZE=%d", esel.size()));
215
216 throw std::length_error("ESEL data too short");
217 }
218
219 for (size_t i = pelStart; i < esel.size(); i += 3)
220 {
221 if (i + 1 < esel.size())
222 {
223 byteString = esel.substr(i, 2);
224 data.push_back(std::stoi(byteString, nullptr, 16));
225 }
226 else
227 {
228 log<level::ERR>("ESEL data too short",
229 entry("ESEL_SIZE=%d", esel.size()));
230 throw std::length_error("ESEL data too short");
231 }
232 }
233
234 return data;
235}
236
Matt Spinler4e8078c2019-07-09 13:22:32 -0500237void Manager::erase(uint32_t obmcLogID)
238{
Matt Spinler475e5742019-07-18 16:09:49 -0500239 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
240
241 _repo.remove(id);
Matt Spinler4e8078c2019-07-09 13:22:32 -0500242}
243
244bool Manager::isDeleteProhibited(uint32_t obmcLogID)
245{
246 return false;
247}
248
Matt Spinler56ad2a02020-03-26 14:00:52 -0500249PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc)
250{
251 PelFFDC pelFFDC;
252
253 std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) {
254 PelFFDCfile pf;
255 pf.subType = std::get<ffdcSubtypePos>(f);
256 pf.version = std::get<ffdcVersionPos>(f);
257 pf.fd = std::get<ffdcFDPos>(f);
258
259 switch (std::get<ffdcFormatPos>(f))
260 {
261 case Create::FFDCFormat::JSON:
262 pf.format = UserDataFormat::json;
263 break;
264 case Create::FFDCFormat::CBOR:
265 pf.format = UserDataFormat::cbor;
266 break;
267 case Create::FFDCFormat::Text:
268 pf.format = UserDataFormat::text;
269 break;
270 case Create::FFDCFormat::Custom:
271 pf.format = UserDataFormat::custom;
272 break;
273 }
274
275 pelFFDC.push_back(pf);
276 });
277
278 return pelFFDC;
279}
280
Matt Spinler4e8078c2019-07-09 13:22:32 -0500281void Manager::createPEL(const std::string& message, uint32_t obmcLogID,
282 uint64_t timestamp,
283 phosphor::logging::Entry::Level severity,
284 const std::vector<std::string>& additionalData,
Matt Spinler56ad2a02020-03-26 14:00:52 -0500285 const std::vector<std::string>& associations,
286 const FFDCEntries& ffdc)
Matt Spinler4e8078c2019-07-09 13:22:32 -0500287{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800288 auto entry = _registry.lookup(message, rg::LookupType::name);
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600289 std::string msg;
Matt Spinler67456c22019-10-21 12:22:49 -0500290
291 if (entry)
292 {
293 AdditionalData ad{additionalData};
294
Matt Spinler56ad2a02020-03-26 14:00:52 -0500295 auto pelFFDC = convertToPelFFDC(ffdc);
296
Matt Spinlera34ab722019-12-16 10:39:32 -0600297 auto pel = std::make_unique<openpower::pels::PEL>(
Matt Spinler56ad2a02020-03-26 14:00:52 -0500298 *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
Matt Spinler67456c22019-10-21 12:22:49 -0500299
300 _repo.add(pel);
Matt Spinler67456c22019-10-21 12:22:49 -0500301
Matt Spinler7e727a32020-07-07 15:00:17 -0500302 if (_repo.sizeWarning())
303 {
304 scheduleRepoPrune();
305 }
306
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600307 auto src = pel->primarySRC();
308 if (src)
309 {
310 using namespace std::literals::string_literals;
Matt Spinler19e72902020-01-24 11:05:20 -0600311 auto id = getNumberString("0x%08X", pel->id());
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600312 msg = "Created PEL "s + id + " with SRC "s + (*src)->asciiString();
313 while (msg.back() == ' ')
314 {
315 msg.pop_back();
316 }
317 log<level::INFO>(msg.c_str());
318 }
319 }
320 else
321 {
322 // TODO ibm-openbmc/dev/1151: Create a new PEL for this case.
323 // For now, just trace it.
324 msg = "Event not found in PEL message registry: " + message;
325 log<level::INFO>(msg.c_str());
326 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500327}
328
Matt Spinlera34ab722019-12-16 10:39:32 -0600329sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
330{
331 Repository::LogID id{Repository::LogID::Pel(pelID)};
332 std::optional<int> fd;
333
Matt Spinler5f5352e2020-03-05 16:23:27 -0600334 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID));
335
Matt Spinlera34ab722019-12-16 10:39:32 -0600336 try
337 {
338 fd = _repo.getPELFD(id);
339 }
340 catch (std::exception& e)
341 {
342 throw common_error::InternalFailure();
343 }
344
345 if (!fd)
346 {
347 throw common_error::InvalidArgument();
348 }
349
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600350 scheduleFDClose(*fd);
351
Matt Spinlera34ab722019-12-16 10:39:32 -0600352 return *fd;
353}
354
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600355void Manager::scheduleFDClose(int fd)
356{
357 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>(
Matt Spinlerff9cec22020-07-15 13:06:35 -0500358 _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd,
359 std::placeholders::_1));
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600360}
361
362void Manager::closeFD(int fd, sdeventplus::source::EventBase& source)
363{
364 close(fd);
365 _fdCloserEventSource.reset();
366}
367
Matt Spinlera34ab722019-12-16 10:39:32 -0600368std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID)
369{
370 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
371 std::optional<std::vector<uint8_t>> data;
372
Matt Spinler5f5352e2020-03-05 16:23:27 -0600373 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID));
374
Matt Spinlera34ab722019-12-16 10:39:32 -0600375 try
376 {
377 data = _repo.getPELData(id);
378 }
379 catch (std::exception& e)
380 {
381 throw common_error::InternalFailure();
382 }
383
384 if (!data)
385 {
386 throw common_error::InvalidArgument();
387 }
388
389 return *data;
390}
391
392void Manager::hostAck(uint32_t pelID)
393{
394 Repository::LogID id{Repository::LogID::Pel(pelID)};
395
Matt Spinler5f5352e2020-03-05 16:23:27 -0600396 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID));
397
Matt Spinlera34ab722019-12-16 10:39:32 -0600398 if (!_repo.hasPEL(id))
399 {
400 throw common_error::InvalidArgument();
401 }
402
403 if (_hostNotifier)
404 {
405 _hostNotifier->ackPEL(pelID);
406 }
407}
408
409void Manager::hostReject(uint32_t pelID, RejectionReason reason)
410{
411 Repository::LogID id{Repository::LogID::Pel(pelID)};
412
Matt Spinler5f5352e2020-03-05 16:23:27 -0600413 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID),
414 entry("REASON=%d", static_cast<int>(reason)));
415
Matt Spinlera34ab722019-12-16 10:39:32 -0600416 if (!_repo.hasPEL(id))
417 {
418 throw common_error::InvalidArgument();
419 }
420
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600421 if (reason == RejectionReason::BadPEL)
Matt Spinlera34ab722019-12-16 10:39:32 -0600422 {
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600423 AdditionalData data;
424 data.add("BAD_ID", getNumberString("0x%08X", pelID));
425 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost",
426 Entry::Level::Informational, data);
427 if (_hostNotifier)
Matt Spinlera34ab722019-12-16 10:39:32 -0600428 {
429 _hostNotifier->setBadPEL(pelID);
430 }
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600431 }
432 else if ((reason == RejectionReason::HostFull) && _hostNotifier)
433 {
434 _hostNotifier->setHostFull(pelID);
Matt Spinlera34ab722019-12-16 10:39:32 -0600435 }
436}
437
Matt Spinler7e727a32020-07-07 15:00:17 -0500438void Manager::scheduleRepoPrune()
439{
Matt Spinler7e727a32020-07-07 15:00:17 -0500440 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>(
Matt Spinlerff9cec22020-07-15 13:06:35 -0500441 _event, std::bind(std::mem_fn(&Manager::pruneRepo), this,
442 std::placeholders::_1));
Matt Spinler7e727a32020-07-07 15:00:17 -0500443}
444
445void Manager::pruneRepo(sdeventplus::source::EventBase& source)
446{
447 auto idsToDelete = _repo.prune();
448
449 // Remove the OpenBMC event logs for the PELs that were just removed.
450 std::for_each(idsToDelete.begin(), idsToDelete.end(),
451 [this](auto id) { this->_logManager.erase(id); });
452
453 _repoPrunerEventSource.reset();
454}
455
Matt Spinlerff9cec22020-07-15 13:06:35 -0500456void Manager::setupPELDeleteWatch()
457{
458 _pelFileDeleteFD = inotify_init1(IN_NONBLOCK);
459 if (-1 == _pelFileDeleteFD)
460 {
461 auto e = errno;
462 std::string msg =
463 "inotify_init1 failed with errno " + std::to_string(e);
464 log<level::ERR>(msg.c_str());
465 abort();
466 }
467
468 _pelFileDeleteWatchFD = inotify_add_watch(
469 _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE);
470 if (-1 == _pelFileDeleteWatchFD)
471 {
472 auto e = errno;
473 std::string msg =
474 "inotify_add_watch failed with error " + std::to_string(e);
475 log<level::ERR>(msg.c_str());
476 abort();
477 }
478
479 _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>(
480 _event, _pelFileDeleteFD, EPOLLIN,
481 std::bind(std::mem_fn(&Manager::pelFileDeleted), this,
482 std::placeholders::_1, std::placeholders::_2,
483 std::placeholders::_3));
484}
485
486void Manager::pelFileDeleted(sdeventplus::source::IO& io, int fd,
487 uint32_t revents)
488{
489 if (!(revents & EPOLLIN))
490 {
491 return;
492 }
493
494 // An event for 1 PEL uses 48B. When all PELs are deleted at once,
495 // as many events as there is room for can be handled in one callback.
496 // A size of 2000 will allow 41 to be processed, with additional
497 // callbacks being needed to process the remaining ones.
498 std::array<uint8_t, 2000> data;
499 auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size());
500 if (bytesRead < 0)
501 {
502 auto e = errno;
503 std::string msg = "Failed reading data from inotify event, errno = " +
504 std::to_string(e);
505 log<level::ERR>(msg.c_str());
506 abort();
507 }
508
509 auto offset = 0;
510 while (offset < bytesRead)
511 {
512 auto event = reinterpret_cast<inotify_event*>(&data[offset]);
513 if (event->mask & IN_DELETE)
514 {
515 std::string filename{event->name};
516
517 // Get the PEL ID from the filename and tell the
518 // repo it's been removed, and then delete the BMC
519 // event log if it's there.
520 auto pos = filename.find_first_of('_');
521 if (pos != std::string::npos)
522 {
523 try
524 {
525 auto idString = filename.substr(pos + 1);
526 auto pelID = std::stoul(idString, nullptr, 16);
527
528 Repository::LogID id{Repository::LogID::Pel(pelID)};
529 auto removedLogID = _repo.remove(id);
530 if (removedLogID)
531 {
532 _logManager.erase(removedLogID->obmcID.id);
533 }
534 }
535 catch (const std::exception& e)
536 {
537 log<level::INFO>("Could not find PEL ID from its filename",
538 entry("FILENAME=%s", filename.c_str()));
539 }
540 }
541 }
542
543 offset += offsetof(inotify_event, name) + event->len;
544 }
545}
Matt Spinler4e8078c2019-07-09 13:22:32 -0500546} // namespace pels
547} // namespace openpower