1 /**
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  */
16 #include "manager.hpp"
17 
18 #include "additional_data.hpp"
19 #include "json_utils.hpp"
20 #include "pel.hpp"
21 #include "service_indicators.hpp"
22 
23 #include <fmt/format.h>
24 #include <sys/inotify.h>
25 #include <unistd.h>
26 
27 #include <filesystem>
28 #include <fstream>
29 #include <xyz/openbmc_project/Common/error.hpp>
30 #include <xyz/openbmc_project/Logging/Create/server.hpp>
31 
32 namespace openpower
33 {
34 namespace pels
35 {
36 
37 using namespace phosphor::logging;
38 namespace fs = std::filesystem;
39 namespace rg = openpower::pels::message;
40 
41 namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
42 
43 using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create;
44 
45 namespace additional_data
46 {
47 constexpr auto rawPEL = "RAWPEL";
48 constexpr auto esel = "ESEL";
49 constexpr auto error = "ERROR_NAME";
50 } // namespace additional_data
51 
52 constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default";
53 
54 Manager::~Manager()
55 {
56     if (_pelFileDeleteFD != -1)
57     {
58         if (_pelFileDeleteWatchFD != -1)
59         {
60             inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD);
61         }
62         close(_pelFileDeleteFD);
63     }
64 }
65 
66 void Manager::create(const std::string& message, uint32_t obmcLogID,
67                      uint64_t timestamp, Entry::Level severity,
68                      const std::vector<std::string>& additionalData,
69                      const std::vector<std::string>& associations,
70                      const FFDCEntries& ffdc)
71 {
72     AdditionalData ad{additionalData};
73 
74     // If a PEL was passed in via a filename or in an ESEL,
75     // use that.  Otherwise, create one.
76     auto rawPelPath = ad.getValue(additional_data::rawPEL);
77     if (rawPelPath)
78     {
79         addRawPEL(*rawPelPath, obmcLogID);
80     }
81     else
82     {
83         auto esel = ad.getValue(additional_data::esel);
84         if (esel)
85         {
86             addESELPEL(*esel, obmcLogID);
87         }
88         else
89         {
90             createPEL(message, obmcLogID, timestamp, severity, additionalData,
91                       associations, ffdc);
92         }
93     }
94 
95     setEntryPath(obmcLogID);
96     setServiceProviderNotifyFlag(obmcLogID);
97 }
98 
99 void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID)
100 {
101     if (fs::exists(rawPelPath))
102     {
103         std::ifstream file(rawPelPath, std::ios::in | std::ios::binary);
104 
105         auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file),
106                                          std::istreambuf_iterator<char>());
107         if (file.fail())
108         {
109             log<level::ERR>("Filesystem error reading a raw PEL",
110                             entry("PELFILE=%s", rawPelPath.c_str()),
111                             entry("OBMCLOGID=%d", obmcLogID));
112             // TODO, Decide what to do here. Maybe nothing.
113             return;
114         }
115 
116         file.close();
117 
118         addPEL(data, obmcLogID);
119 
120         std::error_code ec;
121         fs::remove(rawPelPath, ec);
122     }
123     else
124     {
125         log<level::ERR>("Raw PEL file from BMC event log does not exist",
126                         entry("PELFILE=%s", (rawPelPath).c_str()),
127                         entry("OBMCLOGID=%d", obmcLogID));
128     }
129 }
130 
131 void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID)
132 {
133     auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID);
134     if (pel->valid())
135     {
136         // Assign Id other than to Hostbot PEL
137         if ((pel->privateHeader()).creatorID() !=
138             static_cast<uint8_t>(CreatorID::hostboot))
139         {
140             pel->assignID();
141         }
142         else
143         {
144             const Repository::LogID id{Repository::LogID::Pel(pel->id())};
145             auto result = _repo.hasPEL(id);
146             if (result)
147             {
148                 log<level::WARNING>(
149                     fmt::format("Duplicate HostBoot PEL Id {:#X} found; "
150                                 "moving it to archive folder",
151                                 pel->id())
152                         .c_str());
153 
154                 _repo.archivePEL(*pel);
155                 return;
156             }
157         }
158 
159         // PELs created by others still need this field set by us.
160         pel->setCommitTime();
161 
162         // Update System Info to Extended User Data
163         pel->updateSysInfoInExtendedUserDataSection(*_dataIface);
164 
165         try
166         {
167             log<level::DEBUG>(
168                 fmt::format("Adding external PEL {:#x} (BMC ID {}) to repo",
169                             pel->id(), obmcLogID)
170                     .c_str());
171 
172             _repo.add(pel);
173 
174             if (_repo.sizeWarning())
175             {
176                 scheduleRepoPrune();
177             }
178 
179             // Activate any resulting service indicators if necessary
180             auto policy = service_indicators::getPolicy(*_dataIface);
181             policy->activate(*pel);
182         }
183         catch (std::exception& e)
184         {
185             // Probably a full or r/o filesystem, not much we can do.
186             log<level::ERR>("Unable to add PEL to Repository",
187                             entry("PEL_ID=0x%X", pel->id()));
188         }
189 
190         // Check if firmware should quiesce system due to error
191         checkPelAndQuiesce(pel);
192         updateEventId(pel);
193         updateResolution(pel);
194         createPELEntry(obmcLogID);
195     }
196     else
197     {
198         log<level::ERR>("Invalid PEL received from the host",
199                         entry("OBMCLOGID=%d", obmcLogID));
200 
201         AdditionalData ad;
202         ad.add("PLID", getNumberString("0x%08X", pel->plid()));
203         ad.add("OBMC_LOG_ID", std::to_string(obmcLogID));
204         ad.add("PEL_SIZE", std::to_string(pelData.size()));
205 
206         std::string asciiString;
207         auto src = pel->primarySRC();
208         if (src)
209         {
210             asciiString = (*src)->asciiString();
211         }
212 
213         ad.add("SRC", asciiString);
214 
215         _eventLogger.log("org.open_power.Logging.Error.BadHostPEL",
216                          Entry::Level::Error, ad);
217 
218         // Save it to a file for debug in the lab.  Just keep the latest.
219         // Not adding it to the PEL because it could already be max size
220         // and don't want to truncate an already invalid PEL.
221         std::ofstream pelFile{getPELRepoPath() / "badPEL"};
222         pelFile.write(reinterpret_cast<const char*>(pelData.data()),
223                       pelData.size());
224     }
225 }
226 
227 void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID)
228 {
229     std::vector<uint8_t> data;
230 
231     log<level::DEBUG>("Adding PEL from ESEL",
232                       entry("OBMC_LOG_ID=%d", obmcLogID));
233 
234     try
235     {
236         data = std::move(eselToRawData(esel));
237     }
238     catch (std::exception& e)
239     {
240         // Try to add it below anyway, so it follows the usual bad data path.
241         log<level::ERR>("Problems converting ESEL string to a byte vector");
242     }
243 
244     addPEL(data, obmcLogID);
245 }
246 
247 std::vector<uint8_t> Manager::eselToRawData(const std::string& esel)
248 {
249     std::vector<uint8_t> data;
250     std::string byteString;
251 
252     // As the eSEL string looks like: "50 48 00 ab ..." there are 3
253     // characters per raw byte, and since the actual PEL data starts
254     // at the 16th byte, the code will grab the PEL data starting at
255     // offset 48 in the string.
256     static constexpr size_t pelStart = 16 * 3;
257 
258     if (esel.size() <= pelStart)
259     {
260         log<level::ERR>("ESEL data too short",
261                         entry("ESEL_SIZE=%d", esel.size()));
262 
263         throw std::length_error("ESEL data too short");
264     }
265 
266     for (size_t i = pelStart; i < esel.size(); i += 3)
267     {
268         if (i + 1 < esel.size())
269         {
270             byteString = esel.substr(i, 2);
271             data.push_back(std::stoi(byteString, nullptr, 16));
272         }
273         else
274         {
275             log<level::ERR>("ESEL data too short",
276                             entry("ESEL_SIZE=%d", esel.size()));
277             throw std::length_error("ESEL data too short");
278         }
279     }
280 
281     return data;
282 }
283 
284 void Manager::erase(uint32_t obmcLogID)
285 {
286     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
287 
288     auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
289     _pelEntries.erase(path);
290     _repo.remove(id);
291 }
292 
293 bool Manager::isDeleteProhibited(uint32_t /*obmcLogID*/)
294 {
295     return false;
296 }
297 
298 PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc)
299 {
300     PelFFDC pelFFDC;
301 
302     std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) {
303         PelFFDCfile pf;
304         pf.subType = std::get<ffdcSubtypePos>(f);
305         pf.version = std::get<ffdcVersionPos>(f);
306         pf.fd = std::get<ffdcFDPos>(f);
307 
308         switch (std::get<ffdcFormatPos>(f))
309         {
310             case Create::FFDCFormat::JSON:
311                 pf.format = UserDataFormat::json;
312                 break;
313             case Create::FFDCFormat::CBOR:
314                 pf.format = UserDataFormat::cbor;
315                 break;
316             case Create::FFDCFormat::Text:
317                 pf.format = UserDataFormat::text;
318                 break;
319             case Create::FFDCFormat::Custom:
320                 pf.format = UserDataFormat::custom;
321                 break;
322         }
323 
324         pelFFDC.push_back(pf);
325     });
326 
327     return pelFFDC;
328 }
329 
330 void Manager::createPEL(const std::string& message, uint32_t obmcLogID,
331                         uint64_t timestamp,
332                         phosphor::logging::Entry::Level severity,
333                         const std::vector<std::string>& additionalData,
334                         const std::vector<std::string>& /*associations*/,
335                         const FFDCEntries& ffdc)
336 {
337     auto entry = _registry.lookup(message, rg::LookupType::name);
338     auto pelFFDC = convertToPelFFDC(ffdc);
339     AdditionalData ad{additionalData};
340     std::string msg;
341 
342     if (!entry)
343     {
344         // Instead, get the default entry that means there is no
345         // other matching entry.  This error will still use the
346         // AdditionalData values of the original error, and this
347         // code will add the error message value that wasn't found
348         // to this AD.  This way, there will at least be a PEL,
349         // possibly with callouts, to allow users to debug the
350         // issue that caused the error even without its own PEL.
351         msg = "Event not found in PEL message registry: " + message;
352         log<level::INFO>(msg.c_str());
353 
354         entry = _registry.lookup(defaultLogMessage, rg::LookupType::name);
355         if (!entry)
356         {
357             log<level::ERR>("Default event not found in PEL message registry");
358             return;
359         }
360 
361         ad.add(additional_data::error, message);
362     }
363 
364     auto pel = std::make_unique<openpower::pels::PEL>(
365         *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
366 
367     _repo.add(pel);
368 
369     if (_repo.sizeWarning())
370     {
371         scheduleRepoPrune();
372     }
373 
374     auto src = pel->primarySRC();
375     if (src)
376     {
377         auto msg =
378             fmt::format("Created PEL {:#x} (BMC ID {}) with SRC {}", pel->id(),
379                         pel->obmcLogID(), (*src)->asciiString());
380         while (msg.back() == ' ')
381         {
382             msg.pop_back();
383         }
384         log<level::INFO>(msg.c_str());
385     }
386 
387     // Activate any resulting service indicators if necessary
388     auto policy = service_indicators::getPolicy(*_dataIface);
389     policy->activate(*pel);
390 
391     // Check if firmware should quiesce system due to error
392     checkPelAndQuiesce(pel);
393     updateEventId(pel);
394     updateResolution(pel);
395     createPELEntry(obmcLogID);
396 }
397 
398 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
399 {
400     Repository::LogID id{Repository::LogID::Pel(pelID)};
401     std::optional<int> fd;
402 
403     log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID));
404 
405     try
406     {
407         fd = _repo.getPELFD(id);
408     }
409     catch (std::exception& e)
410     {
411         throw common_error::InternalFailure();
412     }
413 
414     if (!fd)
415     {
416         throw common_error::InvalidArgument();
417     }
418 
419     scheduleFDClose(*fd);
420 
421     return *fd;
422 }
423 
424 void Manager::scheduleFDClose(int fd)
425 {
426     _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>(
427         _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd,
428                           std::placeholders::_1));
429 }
430 
431 void Manager::closeFD(int fd, sdeventplus::source::EventBase& /*source*/)
432 {
433     close(fd);
434     _fdCloserEventSource.reset();
435 }
436 
437 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID)
438 {
439     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
440     std::optional<std::vector<uint8_t>> data;
441 
442     log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID));
443 
444     try
445     {
446         data = _repo.getPELData(id);
447     }
448     catch (std::exception& e)
449     {
450         throw common_error::InternalFailure();
451     }
452 
453     if (!data)
454     {
455         throw common_error::InvalidArgument();
456     }
457 
458     return *data;
459 }
460 
461 void Manager::hostAck(uint32_t pelID)
462 {
463     Repository::LogID id{Repository::LogID::Pel(pelID)};
464 
465     log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID));
466 
467     if (!_repo.hasPEL(id))
468     {
469         throw common_error::InvalidArgument();
470     }
471 
472     if (_hostNotifier)
473     {
474         _hostNotifier->ackPEL(pelID);
475     }
476 }
477 
478 void Manager::hostReject(uint32_t pelID, RejectionReason reason)
479 {
480     Repository::LogID id{Repository::LogID::Pel(pelID)};
481 
482     log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID),
483                       entry("REASON=%d", static_cast<int>(reason)));
484 
485     if (!_repo.hasPEL(id))
486     {
487         throw common_error::InvalidArgument();
488     }
489 
490     if (reason == RejectionReason::BadPEL)
491     {
492         AdditionalData data;
493         data.add("BAD_ID", getNumberString("0x%08X", pelID));
494         _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost",
495                          Entry::Level::Informational, data);
496         if (_hostNotifier)
497         {
498             _hostNotifier->setBadPEL(pelID);
499         }
500     }
501     else if ((reason == RejectionReason::HostFull) && _hostNotifier)
502     {
503         _hostNotifier->setHostFull(pelID);
504     }
505 }
506 
507 void Manager::scheduleRepoPrune()
508 {
509     _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>(
510         _event, std::bind(std::mem_fn(&Manager::pruneRepo), this,
511                           std::placeholders::_1));
512 }
513 
514 void Manager::pruneRepo(sdeventplus::source::EventBase& /*source*/)
515 {
516     auto idsToDelete = _repo.prune();
517 
518     // Remove the OpenBMC event logs for the PELs that were just removed.
519     std::for_each(idsToDelete.begin(), idsToDelete.end(),
520                   [this](auto id) { this->_logManager.erase(id); });
521 
522     _repoPrunerEventSource.reset();
523 }
524 
525 void Manager::setupPELDeleteWatch()
526 {
527     _pelFileDeleteFD = inotify_init1(IN_NONBLOCK);
528     if (-1 == _pelFileDeleteFD)
529     {
530         auto e = errno;
531         std::string msg =
532             "inotify_init1 failed with errno " + std::to_string(e);
533         log<level::ERR>(msg.c_str());
534         abort();
535     }
536 
537     _pelFileDeleteWatchFD = inotify_add_watch(
538         _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE);
539     if (-1 == _pelFileDeleteWatchFD)
540     {
541         auto e = errno;
542         std::string msg =
543             "inotify_add_watch failed with error " + std::to_string(e);
544         log<level::ERR>(msg.c_str());
545         abort();
546     }
547 
548     _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>(
549         _event, _pelFileDeleteFD, EPOLLIN,
550         std::bind(std::mem_fn(&Manager::pelFileDeleted), this,
551                   std::placeholders::_1, std::placeholders::_2,
552                   std::placeholders::_3));
553 }
554 
555 void Manager::pelFileDeleted(sdeventplus::source::IO& /*io*/, int /*fd*/,
556                              uint32_t revents)
557 {
558     if (!(revents & EPOLLIN))
559     {
560         return;
561     }
562 
563     // An event for 1 PEL uses 48B. When all PELs are deleted at once,
564     // as many events as there is room for can be handled in one callback.
565     // A size of 2000 will allow 41 to be processed, with additional
566     // callbacks being needed to process the remaining ones.
567     std::array<uint8_t, 2000> data{};
568     auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size());
569     if (bytesRead < 0)
570     {
571         auto e = errno;
572         std::string msg = "Failed reading data from inotify event, errno = " +
573                           std::to_string(e);
574         log<level::ERR>(msg.c_str());
575         abort();
576     }
577 
578     auto offset = 0;
579     while (offset < bytesRead)
580     {
581         auto event = reinterpret_cast<inotify_event*>(&data[offset]);
582         if (event->mask & IN_DELETE)
583         {
584             std::string filename{event->name};
585 
586             // Get the PEL ID from the filename and tell the
587             // repo it's been removed, and then delete the BMC
588             // event log if it's there.
589             auto pos = filename.find_first_of('_');
590             if (pos != std::string::npos)
591             {
592                 try
593                 {
594                     auto idString = filename.substr(pos + 1);
595                     auto pelID = std::stoul(idString, nullptr, 16);
596 
597                     Repository::LogID id{Repository::LogID::Pel(pelID)};
598                     auto removedLogID = _repo.remove(id);
599                     if (removedLogID)
600                     {
601                         _logManager.erase(removedLogID->obmcID.id);
602                     }
603                 }
604                 catch (const std::exception& e)
605                 {
606                     log<level::INFO>("Could not find PEL ID from its filename",
607                                      entry("FILENAME=%s", filename.c_str()));
608                 }
609             }
610         }
611 
612         offset += offsetof(inotify_event, name) + event->len;
613     }
614 }
615 
616 std::tuple<uint32_t, uint32_t> Manager::createPELWithFFDCFiles(
617     std::string message, Entry::Level severity,
618     std::map<std::string, std::string> additionalData,
619     std::vector<std::tuple<
620         sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
621         uint8_t, uint8_t, sdbusplus::message::unix_fd>>
622         fFDC)
623 {
624     _logManager.createWithFFDC(message, severity, additionalData, fFDC);
625 
626     return {_logManager.lastEntryID(), _repo.lastPelID()};
627 }
628 
629 void Manager::checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel)
630 {
631     if ((pel->userHeader().severity() ==
632          static_cast<uint8_t>(SeverityType::nonError)) ||
633         (pel->userHeader().severity() ==
634          static_cast<uint8_t>(SeverityType::recovered)))
635     {
636         log<level::DEBUG>(
637             "PEL severity informational or recovered. no quiesce needed");
638         return;
639     }
640     if (!_logManager.isQuiesceOnErrorEnabled())
641     {
642         log<level::DEBUG>("QuiesceOnHwError not enabled, no quiesce needed");
643         return;
644     }
645 
646     // Now check if it has any type of callout
647     if (pel->isCalloutPresent())
648     {
649         log<level::INFO>(
650             "QuiesceOnHwError enabled, PEL severity not nonError or recovered, "
651             "and callout is present");
652 
653         _logManager.quiesceOnError(pel->obmcLogID());
654     }
655 }
656 
657 std::string Manager::getEventId(const openpower::pels::PEL& pel) const
658 {
659     std::string str;
660     auto src = pel.primarySRC();
661     if (src)
662     {
663         const auto& hexwords = (*src)->hexwordData();
664 
665         std::string refcode = (*src)->asciiString();
666         size_t pos = refcode.find_last_not_of(0x20);
667         if (pos != std::string::npos)
668         {
669             refcode.erase(pos + 1);
670         }
671         str = refcode;
672 
673         for (auto& value : hexwords)
674         {
675             str += " ";
676             str += getNumberString("%08X", value);
677         }
678     }
679     return str;
680 }
681 
682 void Manager::updateEventId(std::unique_ptr<openpower::pels::PEL>& pel)
683 {
684     std::string eventIdStr = getEventId(*pel);
685 
686     auto entryN = _logManager.entries.find(pel->obmcLogID());
687     if (entryN != _logManager.entries.end())
688     {
689         entryN->second->eventId(eventIdStr);
690     }
691 }
692 
693 std::string Manager::getResolution(const openpower::pels::PEL& pel) const
694 {
695     std::string str;
696     std::string resolution;
697     auto src = pel.primarySRC();
698     if (src)
699     {
700         // First extract the callout pointer and then go through
701         const auto& callouts = (*src)->callouts();
702         namespace pv = openpower::pels::pel_values;
703         // All PELs dont have callout, check before parsing callout data
704         if (callouts)
705         {
706             const auto& entries = callouts->callouts();
707             // Entry starts with index 1
708             uint8_t index = 1;
709             for (auto& entry : entries)
710             {
711                 resolution += std::to_string(index) + ". ";
712                 // Adding Location code to resolution
713                 if (!entry->locationCode().empty())
714                     resolution +=
715                         "Location Code: " + entry->locationCode() + ", ";
716                 if (entry->fruIdentity())
717                 {
718                     // Get priority and set the resolution string
719                     str = pv::getValue(entry->priority(),
720                                        pel_values::calloutPriorityValues,
721                                        pel_values::registryNamePos);
722                     str[0] = toupper(str[0]);
723                     resolution += "Priority: " + str + ", ";
724                     if (entry->fruIdentity()->getPN().has_value())
725                     {
726                         resolution +=
727                             "PN: " + entry->fruIdentity()->getPN().value() +
728                             ", ";
729                     }
730                     if (entry->fruIdentity()->getSN().has_value())
731                     {
732                         resolution +=
733                             "SN: " + entry->fruIdentity()->getSN().value() +
734                             ", ";
735                     }
736                     if (entry->fruIdentity()->getCCIN().has_value())
737                     {
738                         resolution +=
739                             "CCIN: " + entry->fruIdentity()->getCCIN().value() +
740                             ", ";
741                     }
742                     // Add the maintenance procedure
743                     if (entry->fruIdentity()->getMaintProc().has_value())
744                     {
745                         resolution +=
746                             "Procedure: " +
747                             entry->fruIdentity()->getMaintProc().value() + ", ";
748                     }
749                 }
750                 resolution.resize(resolution.size() - 2);
751                 resolution += "\n";
752                 index++;
753             }
754         }
755     }
756     return resolution;
757 }
758 
759 void Manager::updateResolution(std::unique_ptr<openpower::pels::PEL>& pel)
760 {
761     std::string callouts = getResolution(*pel);
762     auto entryN = _logManager.entries.find(pel->obmcLogID());
763     if (entryN != _logManager.entries.end())
764     {
765         entryN->second->resolution(callouts);
766     }
767 }
768 
769 void Manager::setEntryPath(uint32_t obmcLogID)
770 {
771     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
772     if (auto attributes = _repo.getPELAttributes(id); attributes)
773     {
774         auto& attr = attributes.value().get();
775         auto entry = _logManager.entries.find(obmcLogID);
776         if (entry != _logManager.entries.end())
777         {
778             entry->second->path(attr.path);
779         }
780     }
781 }
782 
783 void Manager::setServiceProviderNotifyFlag(uint32_t obmcLogID)
784 {
785     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
786     if (auto attributes = _repo.getPELAttributes(id); attributes)
787     {
788         auto& attr = attributes.value().get();
789         auto entry = _logManager.entries.find(obmcLogID);
790         if (entry != _logManager.entries.end())
791         {
792             entry->second->serviceProviderNotify(
793                 attr.actionFlags.test(callHomeFlagBit));
794         }
795     }
796 }
797 
798 void Manager::createPELEntry(uint32_t obmcLogID)
799 {
800     std::map<std::string, PropertiesVariant> varData;
801     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
802     if (auto attributes = _repo.getPELAttributes(id); attributes)
803     {
804         namespace pv = openpower::pels::pel_values;
805         auto& attr = attributes.value().get();
806         varData.emplace(std::string("Hidden"),
807                         attr.actionFlags.test(hiddenFlagBit));
808         varData.emplace(
809             std::string("Subsystem"),
810             pv::getValue(attr.subsystem, pel_values::subsystemValues));
811         // Path to create PELEntry Interface is same as PEL
812         auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
813         // Create Interface for PELEntry and set properties
814         auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(),
815                                                    path.c_str(), varData);
816         _pelEntries.emplace(std::move(path), std::move(pelEntry));
817     }
818 }
819 
820 uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId)
821 {
822     Repository::LogID id{Repository::LogID::Obmc(bmcLogId)};
823     if (auto logId = _repo.getLogID(id); !logId.has_value())
824     {
825         throw common_error::InvalidArgument();
826     }
827     else
828     {
829         return logId->pelID.id;
830     }
831 }
832 
833 uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId)
834 {
835     Repository::LogID id{Repository::LogID::Pel(pelId)};
836     if (auto logId = _repo.getLogID(id); !logId.has_value())
837     {
838         throw common_error::InvalidArgument();
839     }
840     else
841     {
842         return logId->obmcID.id;
843     }
844 }
845 
846 } // namespace pels
847 } // namespace openpower
848