1f18bf836SPatrick Venture #include "config.h"
2f18bf836SPatrick Venture 
3f18bf836SPatrick Venture #include "log_manager.hpp"
4f18bf836SPatrick Venture 
5f18bf836SPatrick Venture #include "elog_entry.hpp"
6f18bf836SPatrick Venture #include "elog_meta.hpp"
7f18bf836SPatrick Venture #include "elog_serialize.hpp"
899c2b405SMatt Spinler #include "extensions.hpp"
9f61f2921SMatt Spinler #include "util.hpp"
10f18bf836SPatrick Venture 
111db1bd35SAdriana Kobylak #include <systemd/sd-bus.h>
12d311bc8dSAdriana Kobylak #include <systemd/sd-journal.h>
135f4247f0SAdriana Kobylak #include <unistd.h>
14f18bf836SPatrick Venture 
152544b419SPatrick Williams #include <phosphor-logging/lg2.hpp>
162544b419SPatrick Williams #include <sdbusplus/vtable.hpp>
172544b419SPatrick Williams #include <xyz/openbmc_project/State/Host/server.hpp>
182544b419SPatrick Williams 
1999c2b405SMatt Spinler #include <cassert>
20f18bf836SPatrick Venture #include <chrono>
21f18bf836SPatrick Venture #include <cstdio>
2230047bf9SPatrick Venture #include <cstring>
23f18bf836SPatrick Venture #include <fstream>
2430047bf9SPatrick Venture #include <functional>
25f18bf836SPatrick Venture #include <future>
26f18bf836SPatrick Venture #include <iostream>
2730047bf9SPatrick Venture #include <map>
28f18bf836SPatrick Venture #include <set>
29f18bf836SPatrick Venture #include <string>
30b01a5b4fSPatrick Williams #include <string_view>
31f18bf836SPatrick Venture #include <vector>
32a87c157cSDeepak Kodihalli 
335f4247f0SAdriana Kobylak using namespace std::chrono;
345f285c53SPatrick Williams extern const std::map<
355f285c53SPatrick Williams     phosphor::logging::metadata::Metadata,
365f285c53SPatrick Williams     std::function<phosphor::logging::metadata::associations::Type>>
37f18bf836SPatrick Venture     meta;
381db1bd35SAdriana Kobylak 
398f7941edSAdriana Kobylak namespace phosphor
408f7941edSAdriana Kobylak {
418f7941edSAdriana Kobylak namespace logging
428f7941edSAdriana Kobylak {
4305aae8bcSNagaraju Goruganti namespace internal
4405aae8bcSNagaraju Goruganti {
getLevel(const std::string & errMsg)456fd9dc48SDeepak Kodihalli inline auto getLevel(const std::string& errMsg)
466fd9dc48SDeepak Kodihalli {
476fd9dc48SDeepak Kodihalli     auto reqLevel = Entry::Level::Error; // Default to Error
486fd9dc48SDeepak Kodihalli 
496fd9dc48SDeepak Kodihalli     auto levelmap = g_errLevelMap.find(errMsg);
50f8a5a797SNagaraju Goruganti     if (levelmap != g_errLevelMap.end())
51f8a5a797SNagaraju Goruganti     {
526fd9dc48SDeepak Kodihalli         reqLevel = static_cast<Entry::Level>(levelmap->second);
53f8a5a797SNagaraju Goruganti     }
54f8a5a797SNagaraju Goruganti 
556fd9dc48SDeepak Kodihalli     return reqLevel;
566fd9dc48SDeepak Kodihalli }
576fd9dc48SDeepak Kodihalli 
getRealErrSize()58477b731aSNagaraju Goruganti int Manager::getRealErrSize()
59477b731aSNagaraju Goruganti {
60477b731aSNagaraju Goruganti     return realErrors.size();
61477b731aSNagaraju Goruganti }
62477b731aSNagaraju Goruganti 
getInfoErrSize()63477b731aSNagaraju Goruganti int Manager::getInfoErrSize()
64477b731aSNagaraju Goruganti {
65477b731aSNagaraju Goruganti     return infoErrors.size();
66477b731aSNagaraju Goruganti }
67477b731aSNagaraju Goruganti 
commit(uint64_t transactionId,std::string errMsg)68b50c705cSLei YU uint32_t Manager::commit(uint64_t transactionId, std::string errMsg)
696fd9dc48SDeepak Kodihalli {
706fd9dc48SDeepak Kodihalli     auto level = getLevel(errMsg);
716fd9dc48SDeepak Kodihalli     _commit(transactionId, std::move(errMsg), level);
72b50c705cSLei YU     return entryId;
736fd9dc48SDeepak Kodihalli }
746fd9dc48SDeepak Kodihalli 
commitWithLvl(uint64_t transactionId,std::string errMsg,uint32_t errLvl)75b50c705cSLei YU uint32_t Manager::commitWithLvl(uint64_t transactionId, std::string errMsg,
766fd9dc48SDeepak Kodihalli                                 uint32_t errLvl)
776fd9dc48SDeepak Kodihalli {
786fd9dc48SDeepak Kodihalli     _commit(transactionId, std::move(errMsg),
796fd9dc48SDeepak Kodihalli             static_cast<Entry::Level>(errLvl));
80b50c705cSLei YU     return entryId;
816fd9dc48SDeepak Kodihalli }
826fd9dc48SDeepak Kodihalli 
_commit(uint64_t transactionId,std::string && errMsg,Entry::Level errLvl)83a517197dSPatrick Williams void Manager::_commit(uint64_t transactionId [[maybe_unused]],
84a517197dSPatrick Williams                       std::string&& errMsg, Entry::Level errLvl)
856fd9dc48SDeepak Kodihalli {
86a517197dSPatrick Williams     std::vector<std::string> additionalData{};
87a517197dSPatrick Williams 
88a517197dSPatrick Williams     // When running as a test-case, the system may have a LOT of journal
89a517197dSPatrick Williams     // data and we may not have permissions to do some of the journal sync
90a517197dSPatrick Williams     // operations.  Just skip over them.
91b6b25575SWilliam A. Kennington III     if (!IS_UNIT_TEST)
92b6b25575SWilliam A. Kennington III     {
93b01a5b4fSPatrick Williams         static constexpr auto transactionIdVar =
94b01a5b4fSPatrick Williams             std::string_view{"TRANSACTION_ID"};
9527c87d92SAdriana Kobylak         // Length of 'TRANSACTION_ID' string.
96b01a5b4fSPatrick Williams         static constexpr auto transactionIdVarSize = transactionIdVar.size();
9727c87d92SAdriana Kobylak         // Length of 'TRANSACTION_ID=' string.
98b01a5b4fSPatrick Williams         static constexpr auto transactionIdVarOffset = transactionIdVarSize + 1;
991db1bd35SAdriana Kobylak 
1005f4247f0SAdriana Kobylak         // Flush all the pending log messages into the journal
101271d143dSMatt Spinler         util::journalSync();
102cfd9a7ddSAdriana Kobylak 
103d311bc8dSAdriana Kobylak         sd_journal* j = nullptr;
1048f7941edSAdriana Kobylak         int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
105d311bc8dSAdriana Kobylak         if (rc < 0)
106d311bc8dSAdriana Kobylak         {
1075f285c53SPatrick Williams             lg2::error("Failed to open journal: {ERROR}", "ERROR",
1085f285c53SPatrick Williams                        strerror(-rc));
1098f7941edSAdriana Kobylak             return;
110d311bc8dSAdriana Kobylak         }
111d311bc8dSAdriana Kobylak 
1127298dc23SAdriana Kobylak         std::string transactionIdStr = std::to_string(transactionId);
113d722b3aaSAdriana Kobylak         std::set<std::string> metalist;
114d722b3aaSAdriana Kobylak         auto metamap = g_errMetaMap.find(errMsg);
115d722b3aaSAdriana Kobylak         if (metamap != g_errMetaMap.end())
116d722b3aaSAdriana Kobylak         {
117d722b3aaSAdriana Kobylak             metalist.insert(metamap->second.begin(), metamap->second.end());
118d722b3aaSAdriana Kobylak         }
1197298dc23SAdriana Kobylak 
120db18ebe0SJayanth Othayoth         // Add _PID field information in AdditionalData.
121db18ebe0SJayanth Othayoth         metalist.insert("_PID");
122db18ebe0SJayanth Othayoth 
123d311bc8dSAdriana Kobylak         // Read the journal from the end to get the most recent entry first.
124b6b25575SWilliam A. Kennington III         // The result from the sd_journal_get_data() is of the form
125b6b25575SWilliam A. Kennington III         // VARIABLE=value.
126d311bc8dSAdriana Kobylak         SD_JOURNAL_FOREACH_BACKWARDS(j)
127d311bc8dSAdriana Kobylak         {
128d311bc8dSAdriana Kobylak             const char* data = nullptr;
129d311bc8dSAdriana Kobylak             size_t length = 0;
130d311bc8dSAdriana Kobylak 
1317298dc23SAdriana Kobylak             // Look for the transaction id metadata variable
132b01a5b4fSPatrick Williams             rc = sd_journal_get_data(j, transactionIdVar.data(),
133b01a5b4fSPatrick Williams                                      (const void**)&data, &length);
134d311bc8dSAdriana Kobylak             if (rc < 0)
135d311bc8dSAdriana Kobylak             {
136fbe8872eSAdriana Kobylak                 // This journal entry does not have the TRANSACTION_ID
137fbe8872eSAdriana Kobylak                 // metadata variable.
138d311bc8dSAdriana Kobylak                 continue;
139d311bc8dSAdriana Kobylak             }
1407298dc23SAdriana Kobylak 
141b6b25575SWilliam A. Kennington III             // journald does not guarantee that sd_journal_get_data() returns
142b6b25575SWilliam A. Kennington III             // NULL terminated strings, so need to specify the size to use to
143b6b25575SWilliam A. Kennington III             // compare, use the returned length instead of anything that relies
144b6b25575SWilliam A. Kennington III             // on NULL terminators like strlen(). The data variable is in the
145b6b25575SWilliam A. Kennington III             // form of 'TRANSACTION_ID=1234'. Remove the TRANSACTION_ID
146b6b25575SWilliam A. Kennington III             // characters plus the (=) sign to do the comparison. 'data +
147b6b25575SWilliam A. Kennington III             // transactionIdVarOffset' will be in the form of '1234'. 'length -
148b6b25575SWilliam A. Kennington III             // transactionIdVarOffset' will be the length of '1234'.
14927c87d92SAdriana Kobylak             if ((length <= (transactionIdVarOffset)) ||
150b6b25575SWilliam A. Kennington III                 (transactionIdStr.compare(
151b6b25575SWilliam A. Kennington III                      0, transactionIdStr.size(), data + transactionIdVarOffset,
15227c87d92SAdriana Kobylak                      length - transactionIdVarOffset) != 0))
153d311bc8dSAdriana Kobylak             {
154fbe8872eSAdriana Kobylak                 // The value of the TRANSACTION_ID metadata is not the requested
155fbe8872eSAdriana Kobylak                 // transaction id number.
156d311bc8dSAdriana Kobylak                 continue;
157d311bc8dSAdriana Kobylak             }
158d311bc8dSAdriana Kobylak 
159fbe8872eSAdriana Kobylak             // Search for all metadata variables in the current journal entry.
160fbe8872eSAdriana Kobylak             for (auto i = metalist.cbegin(); i != metalist.cend();)
161fbe8872eSAdriana Kobylak             {
162f18bf836SPatrick Venture                 rc = sd_journal_get_data(j, (*i).c_str(), (const void**)&data,
163f18bf836SPatrick Venture                                          &length);
164fbe8872eSAdriana Kobylak                 if (rc < 0)
165fbe8872eSAdriana Kobylak                 {
166b6b25575SWilliam A. Kennington III                     // Metadata variable not found, check next metadata
167b6b25575SWilliam A. Kennington III                     // variable.
168fbe8872eSAdriana Kobylak                     i++;
169fbe8872eSAdriana Kobylak                     continue;
170fbe8872eSAdriana Kobylak                 }
171fbe8872eSAdriana Kobylak 
172fbe8872eSAdriana Kobylak                 // Metadata variable found, save it and remove it from the set.
173fbe8872eSAdriana Kobylak                 additionalData.emplace_back(data, length);
174fbe8872eSAdriana Kobylak                 i = metalist.erase(i);
175fbe8872eSAdriana Kobylak             }
176fbe8872eSAdriana Kobylak             if (metalist.empty())
177fbe8872eSAdriana Kobylak             {
178fbe8872eSAdriana Kobylak                 // All metadata variables found, break out of journal loop.
1799aa7d789SAdriana Kobylak                 break;
1809aa7d789SAdriana Kobylak             }
181fbe8872eSAdriana Kobylak         }
182fbe8872eSAdriana Kobylak         if (!metalist.empty())
183d311bc8dSAdriana Kobylak         {
184fbe8872eSAdriana Kobylak             // Not all the metadata variables were found in the journal.
185fbe8872eSAdriana Kobylak             for (auto& metaVarStr : metalist)
186fbe8872eSAdriana Kobylak             {
1875f285c53SPatrick Williams                 lg2::info("Failed to find metadata: {META_FIELD}", "META_FIELD",
1885f285c53SPatrick Williams                           metaVarStr);
189d311bc8dSAdriana Kobylak             }
190d311bc8dSAdriana Kobylak         }
191d311bc8dSAdriana Kobylak 
192d311bc8dSAdriana Kobylak         sd_journal_close(j);
193b6b25575SWilliam A. Kennington III     }
194b60e7559SMatt Spinler     createEntry(errMsg, errLvl, additionalData);
195b60e7559SMatt Spinler }
196b60e7559SMatt Spinler 
createEntry(std::string errMsg,Entry::Level errLvl,std::vector<std::string> additionalData,const FFDCEntries & ffdc)197b60e7559SMatt Spinler void Manager::createEntry(std::string errMsg, Entry::Level errLvl,
198c64b7122SMatt Spinler                           std::vector<std::string> additionalData,
199c64b7122SMatt Spinler                           const FFDCEntries& ffdc)
200b60e7559SMatt Spinler {
201b60e7559SMatt Spinler     if (!Extensions::disableDefaultLogCaps())
202b60e7559SMatt Spinler     {
203b60e7559SMatt Spinler         if (errLvl < Entry::sevLowerLimit)
204b60e7559SMatt Spinler         {
205b60e7559SMatt Spinler             if (realErrors.size() >= ERROR_CAP)
206b60e7559SMatt Spinler             {
207b60e7559SMatt Spinler                 erase(realErrors.front());
208b60e7559SMatt Spinler             }
209b60e7559SMatt Spinler         }
210b60e7559SMatt Spinler         else
211b60e7559SMatt Spinler         {
212b60e7559SMatt Spinler             if (infoErrors.size() >= ERROR_INFO_CAP)
213b60e7559SMatt Spinler             {
214b60e7559SMatt Spinler                 erase(infoErrors.front());
215b60e7559SMatt Spinler             }
216b60e7559SMatt Spinler         }
217b60e7559SMatt Spinler     }
218b60e7559SMatt Spinler 
2194ea7f312SAdriana Kobylak     entryId++;
2206fd9dc48SDeepak Kodihalli     if (errLvl >= Entry::sevLowerLimit)
221f8a5a797SNagaraju Goruganti     {
222f8a5a797SNagaraju Goruganti         infoErrors.push_back(entryId);
223f8a5a797SNagaraju Goruganti     }
224e4b0b771SNagaraju Goruganti     else
225e4b0b771SNagaraju Goruganti     {
226e4b0b771SNagaraju Goruganti         realErrors.push_back(entryId);
227e4b0b771SNagaraju Goruganti     }
228c5f0bbd9SAdriana Kobylak     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
229f18bf836SPatrick Venture                   std::chrono::system_clock::now().time_since_epoch())
230f18bf836SPatrick Venture                   .count();
231f18bf836SPatrick Venture     auto objPath = std::string(OBJ_ENTRY) + '/' + std::to_string(entryId);
232a87c157cSDeepak Kodihalli 
23335b46379SDeepak Kodihalli     AssociationList objects{};
234a87c157cSDeepak Kodihalli     processMetadata(errMsg, additionalData, objects);
235a87c157cSDeepak Kodihalli 
236fb978da4SMatt Spinler     auto e = std::make_unique<Entry>(
237fb978da4SMatt Spinler         busLog, objPath, entryId,
238c5f0bbd9SAdriana Kobylak         ms, // Milliseconds since 1970
239fb978da4SMatt Spinler         errLvl, std::move(errMsg), std::move(additionalData),
240fb978da4SMatt Spinler         std::move(objects), fwVersion, getEntrySerializePath(entryId), *this);
241fb978da4SMatt Spinler 
242fb978da4SMatt Spinler     serialize(*e);
24399c2b405SMatt Spinler 
244601b8113SMatt Spinler     if (isQuiesceOnErrorEnabled() && (errLvl < Entry::sevLowerLimit) &&
245601b8113SMatt Spinler         isCalloutPresent(*e))
246c0c500efSAndrew Geissler     {
24732874543SAndrew Geissler         quiesceOnError(entryId);
248c0c500efSAndrew Geissler     }
249c0c500efSAndrew Geissler 
250e7d271aeSAdriana Kobylak     // Add entry before calling the extensions so that they have access to it
251e7d271aeSAdriana Kobylak     entries.insert(std::make_pair(entryId, std::move(e)));
252e7d271aeSAdriana Kobylak 
253e7d271aeSAdriana Kobylak     doExtensionLogCreate(*entries.find(entryId)->second, ffdc);
254c64b7122SMatt Spinler 
255c64b7122SMatt Spinler     // Note: No need to close the file descriptors in the FFDC.
2561db1bd35SAdriana Kobylak }
2571db1bd35SAdriana Kobylak 
isQuiesceOnErrorEnabled()258c0c500efSAndrew Geissler bool Manager::isQuiesceOnErrorEnabled()
259c0c500efSAndrew Geissler {
260a517197dSPatrick Williams     // When running under tests, the Logging.Settings service will not be
261a517197dSPatrick Williams     // present.  Assume false.
262b6b25575SWilliam A. Kennington III     if (IS_UNIT_TEST)
263b6b25575SWilliam A. Kennington III     {
264a517197dSPatrick Williams         return false;
265b6b25575SWilliam A. Kennington III     }
266a517197dSPatrick Williams 
267c0c500efSAndrew Geissler     std::variant<bool> property;
268c0c500efSAndrew Geissler 
269c0c500efSAndrew Geissler     auto method = this->busLog.new_method_call(
270c0c500efSAndrew Geissler         "xyz.openbmc_project.Settings", "/xyz/openbmc_project/logging/settings",
271c0c500efSAndrew Geissler         "org.freedesktop.DBus.Properties", "Get");
272c0c500efSAndrew Geissler 
273c0c500efSAndrew Geissler     method.append("xyz.openbmc_project.Logging.Settings", "QuiesceOnHwError");
274c0c500efSAndrew Geissler 
275c0c500efSAndrew Geissler     try
276c0c500efSAndrew Geissler     {
277c0c500efSAndrew Geissler         auto reply = this->busLog.call(method);
278c0c500efSAndrew Geissler         reply.read(property);
279c0c500efSAndrew Geissler     }
28045e83521SPatrick Williams     catch (const sdbusplus::exception_t& e)
281c0c500efSAndrew Geissler     {
2825f285c53SPatrick Williams         lg2::error("Error reading QuiesceOnHwError property: {ERROR}", "ERROR",
2835f285c53SPatrick Williams                    e);
2847ba17c34SMatt Spinler         return false;
285c0c500efSAndrew Geissler     }
286c0c500efSAndrew Geissler 
287c0c500efSAndrew Geissler     return std::get<bool>(property);
288c0c500efSAndrew Geissler }
289c0c500efSAndrew Geissler 
isCalloutPresent(const Entry & entry)290e4960ee7SAndrew Geissler bool Manager::isCalloutPresent(const Entry& entry)
291e4960ee7SAndrew Geissler {
292e4960ee7SAndrew Geissler     for (const auto& c : entry.additionalData())
293e4960ee7SAndrew Geissler     {
294e4960ee7SAndrew Geissler         if (c.find("CALLOUT_") != std::string::npos)
295e4960ee7SAndrew Geissler         {
296e4960ee7SAndrew Geissler             return true;
297e4960ee7SAndrew Geissler         }
298e4960ee7SAndrew Geissler     }
299e4960ee7SAndrew Geissler 
300e4960ee7SAndrew Geissler     return false;
301e4960ee7SAndrew Geissler }
302e4960ee7SAndrew Geissler 
findAndRemoveResolvedBlocks()3037f6d4bcfSAndrew Geissler void Manager::findAndRemoveResolvedBlocks()
3047f6d4bcfSAndrew Geissler {
3057f6d4bcfSAndrew Geissler     for (auto& entry : entries)
3067f6d4bcfSAndrew Geissler     {
3077f6d4bcfSAndrew Geissler         if (entry.second->resolved())
3087f6d4bcfSAndrew Geissler         {
3097f6d4bcfSAndrew Geissler             checkAndRemoveBlockingError(entry.first);
3107f6d4bcfSAndrew Geissler         }
3117f6d4bcfSAndrew Geissler     }
3127f6d4bcfSAndrew Geissler }
3137f6d4bcfSAndrew Geissler 
onEntryResolve(sdbusplus::message_t & msg)31445e83521SPatrick Williams void Manager::onEntryResolve(sdbusplus::message_t& msg)
3157f6d4bcfSAndrew Geissler {
3167f6d4bcfSAndrew Geissler     using Interface = std::string;
3177f6d4bcfSAndrew Geissler     using Property = std::string;
3187f6d4bcfSAndrew Geissler     using Value = std::string;
31925acc6caSPatrick Williams     using Properties = std::map<Property, std::variant<Value>>;
3207f6d4bcfSAndrew Geissler 
3217f6d4bcfSAndrew Geissler     Interface interface;
3227f6d4bcfSAndrew Geissler     Properties properties;
3237f6d4bcfSAndrew Geissler 
3247f6d4bcfSAndrew Geissler     msg.read(interface, properties);
3257f6d4bcfSAndrew Geissler 
3267f6d4bcfSAndrew Geissler     for (const auto& p : properties)
3277f6d4bcfSAndrew Geissler     {
3287f6d4bcfSAndrew Geissler         if (p.first == "Resolved")
3297f6d4bcfSAndrew Geissler         {
3307f6d4bcfSAndrew Geissler             findAndRemoveResolvedBlocks();
3317f6d4bcfSAndrew Geissler             return;
3327f6d4bcfSAndrew Geissler         }
3337f6d4bcfSAndrew Geissler     }
3347f6d4bcfSAndrew Geissler }
3357f6d4bcfSAndrew Geissler 
checkAndQuiesceHost()336f6126a78SAndrew Geissler void Manager::checkAndQuiesceHost()
337f6126a78SAndrew Geissler {
338*6ddbf69eSWilly Tu     using Host = sdbusplus::server::xyz::openbmc_project::state::Host;
339a144a27fSPatrick Williams 
340f6126a78SAndrew Geissler     // First check host state
341a144a27fSPatrick Williams     std::variant<Host::HostState> property;
342f6126a78SAndrew Geissler 
343f6126a78SAndrew Geissler     auto method = this->busLog.new_method_call(
344f6126a78SAndrew Geissler         "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
345f6126a78SAndrew Geissler         "org.freedesktop.DBus.Properties", "Get");
346f6126a78SAndrew Geissler 
347f6126a78SAndrew Geissler     method.append("xyz.openbmc_project.State.Host", "CurrentHostState");
348f6126a78SAndrew Geissler 
349f6126a78SAndrew Geissler     try
350f6126a78SAndrew Geissler     {
351f6126a78SAndrew Geissler         auto reply = this->busLog.call(method);
352f6126a78SAndrew Geissler         reply.read(property);
353f6126a78SAndrew Geissler     }
35445e83521SPatrick Williams     catch (const sdbusplus::exception_t& e)
355f6126a78SAndrew Geissler     {
356f6126a78SAndrew Geissler         // Quiescing the host is a "best effort" type function. If unable to
357f6126a78SAndrew Geissler         // read the host state or it comes back empty, just return.
358f6126a78SAndrew Geissler         // The boot block object will still be created and the associations to
359f6126a78SAndrew Geissler         // find the log will be present. Don't want a dependency with
360f6126a78SAndrew Geissler         // phosphor-state-manager service
3615f285c53SPatrick Williams         lg2::info("Error reading QuiesceOnHwError property: {ERROR}", "ERROR",
3625f285c53SPatrick Williams                   e);
363f6126a78SAndrew Geissler         return;
364f6126a78SAndrew Geissler     }
365f6126a78SAndrew Geissler 
366a144a27fSPatrick Williams     auto hostState = std::get<Host::HostState>(property);
367a144a27fSPatrick Williams     if (hostState != Host::HostState::Running)
368f6126a78SAndrew Geissler     {
369f6126a78SAndrew Geissler         return;
370f6126a78SAndrew Geissler     }
371f6126a78SAndrew Geissler 
372f6126a78SAndrew Geissler     auto quiesce = this->busLog.new_method_call(
373f6126a78SAndrew Geissler         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
374f6126a78SAndrew Geissler         "org.freedesktop.systemd1.Manager", "StartUnit");
375f6126a78SAndrew Geissler 
37686a60e91SAndrew Geissler     quiesce.append("obmc-host-graceful-quiesce@0.target");
377f6126a78SAndrew Geissler     quiesce.append("replace");
378f6126a78SAndrew Geissler 
379f6126a78SAndrew Geissler     this->busLog.call_noreply(quiesce);
380f6126a78SAndrew Geissler }
381f6126a78SAndrew Geissler 
quiesceOnError(const uint32_t entryId)38232874543SAndrew Geissler void Manager::quiesceOnError(const uint32_t entryId)
383c0c500efSAndrew Geissler {
38472a1cca2SAndrew Geissler     // Verify we don't already have this entry blocking
3854a375950SMatt Spinler     auto it = find_if(this->blockingErrors.begin(), this->blockingErrors.end(),
3864a375950SMatt Spinler                       [&](const std::unique_ptr<Block>& obj) {
3874a375950SMatt Spinler         return obj->entryId == entryId;
3884a375950SMatt Spinler     });
38972a1cca2SAndrew Geissler     if (it != this->blockingErrors.end())
39072a1cca2SAndrew Geissler     {
39172a1cca2SAndrew Geissler         // Already recorded so just return
3925f285c53SPatrick Williams         lg2::debug(
39372a1cca2SAndrew Geissler             "QuiesceOnError set and callout present but entry already logged");
39472a1cca2SAndrew Geissler         return;
39572a1cca2SAndrew Geissler     }
396e4960ee7SAndrew Geissler 
3975f285c53SPatrick Williams     lg2::info("QuiesceOnError set and callout present");
3986a0ef6f5SAndrew Geissler 
3992544b419SPatrick Williams     auto blockPath = std::string(OBJ_LOGGING) + "/block" +
4002544b419SPatrick Williams                      std::to_string(entryId);
40132874543SAndrew Geissler     auto blockObj = std::make_unique<Block>(this->busLog, blockPath, entryId);
4026a0ef6f5SAndrew Geissler     this->blockingErrors.push_back(std::move(blockObj));
4036a0ef6f5SAndrew Geissler 
4047f6d4bcfSAndrew Geissler     // Register call back if log is resolved
4057f6d4bcfSAndrew Geissler     using namespace sdbusplus::bus::match::rules;
40632874543SAndrew Geissler     auto entryPath = std::string(OBJ_ENTRY) + '/' + std::to_string(entryId);
40745e83521SPatrick Williams     auto callback = std::make_unique<sdbusplus::bus::match_t>(
4087f6d4bcfSAndrew Geissler         this->busLog,
4097f6d4bcfSAndrew Geissler         propertiesChanged(entryPath, "xyz.openbmc_project.Logging.Entry"),
4107f6d4bcfSAndrew Geissler         std::bind(std::mem_fn(&Manager::onEntryResolve), this,
4117f6d4bcfSAndrew Geissler                   std::placeholders::_1));
4127f6d4bcfSAndrew Geissler 
4137f6d4bcfSAndrew Geissler     propChangedEntryCallback.insert(
41432874543SAndrew Geissler         std::make_pair(entryId, std::move(callback)));
4157f6d4bcfSAndrew Geissler 
416f6126a78SAndrew Geissler     checkAndQuiesceHost();
417c0c500efSAndrew Geissler }
418c0c500efSAndrew Geissler 
doExtensionLogCreate(const Entry & entry,const FFDCEntries & ffdc)419c64b7122SMatt Spinler void Manager::doExtensionLogCreate(const Entry& entry, const FFDCEntries& ffdc)
42099c2b405SMatt Spinler {
42199c2b405SMatt Spinler     // Make the association <endpointpath>/<endpointtype> paths
42299c2b405SMatt Spinler     std::vector<std::string> assocs;
42399c2b405SMatt Spinler     for (const auto& [forwardType, reverseType, endpoint] :
42499c2b405SMatt Spinler          entry.associations())
42599c2b405SMatt Spinler     {
42699c2b405SMatt Spinler         std::string e{endpoint};
42799c2b405SMatt Spinler         e += '/' + reverseType;
42899c2b405SMatt Spinler         assocs.push_back(e);
42999c2b405SMatt Spinler     }
43099c2b405SMatt Spinler 
43199c2b405SMatt Spinler     for (auto& create : Extensions::getCreateFunctions())
43299c2b405SMatt Spinler     {
43399c2b405SMatt Spinler         try
43499c2b405SMatt Spinler         {
43599c2b405SMatt Spinler             create(entry.message(), entry.id(), entry.timestamp(),
436c64b7122SMatt Spinler                    entry.severity(), entry.additionalData(), assocs, ffdc);
43799c2b405SMatt Spinler         }
43866491c61SPatrick Williams         catch (const std::exception& e)
43999c2b405SMatt Spinler         {
4405f285c53SPatrick Williams             lg2::error(
4415f285c53SPatrick Williams                 "An extension's create function threw an exception: {ERROR}",
4425f285c53SPatrick Williams                 "ERROR", e);
44399c2b405SMatt Spinler         }
44499c2b405SMatt Spinler     }
44599c2b405SMatt Spinler }
44699c2b405SMatt Spinler 
processMetadata(const std::string &,const std::vector<std::string> & additionalData,AssociationList & objects) const447f40323d0SPatrick Williams void Manager::processMetadata(const std::string& /*errorName*/,
448a87c157cSDeepak Kodihalli                               const std::vector<std::string>& additionalData,
449a87c157cSDeepak Kodihalli                               AssociationList& objects) const
450a87c157cSDeepak Kodihalli {
451a87c157cSDeepak Kodihalli     // additionalData is a list of "metadata=value"
452a87c157cSDeepak Kodihalli     constexpr auto separator = '=';
45334438968SPatrick Venture     for (const auto& entryItem : additionalData)
454a87c157cSDeepak Kodihalli     {
45534438968SPatrick Venture         auto found = entryItem.find(separator);
456a87c157cSDeepak Kodihalli         if (std::string::npos != found)
457a87c157cSDeepak Kodihalli         {
45834438968SPatrick Venture             auto metadata = entryItem.substr(0, found);
459a87c157cSDeepak Kodihalli             auto iter = meta.find(metadata);
460a87c157cSDeepak Kodihalli             if (meta.end() != iter)
461a87c157cSDeepak Kodihalli             {
462a87c157cSDeepak Kodihalli                 (iter->second)(metadata, additionalData, objects);
463a87c157cSDeepak Kodihalli             }
464a87c157cSDeepak Kodihalli         }
465a87c157cSDeepak Kodihalli     }
466a87c157cSDeepak Kodihalli }
467a87c157cSDeepak Kodihalli 
checkAndRemoveBlockingError(uint32_t entryId)468ced6e2a0SAndrew Geissler void Manager::checkAndRemoveBlockingError(uint32_t entryId)
469ced6e2a0SAndrew Geissler {
4707f6d4bcfSAndrew Geissler     // First look for blocking object and remove
4714a375950SMatt Spinler     auto it = find_if(blockingErrors.begin(), blockingErrors.end(),
4724a375950SMatt Spinler                       [&](const std::unique_ptr<Block>& obj) {
4734a375950SMatt Spinler         return obj->entryId == entryId;
4744a375950SMatt Spinler     });
475ced6e2a0SAndrew Geissler     if (it != blockingErrors.end())
476ced6e2a0SAndrew Geissler     {
477ced6e2a0SAndrew Geissler         blockingErrors.erase(it);
478ced6e2a0SAndrew Geissler     }
4797f6d4bcfSAndrew Geissler 
4807f6d4bcfSAndrew Geissler     // Now remove the callback looking for the error to be resolved
4817f6d4bcfSAndrew Geissler     auto resolveFind = propChangedEntryCallback.find(entryId);
4827f6d4bcfSAndrew Geissler     if (resolveFind != propChangedEntryCallback.end())
4837f6d4bcfSAndrew Geissler     {
4847f6d4bcfSAndrew Geissler         propChangedEntryCallback.erase(resolveFind);
4857f6d4bcfSAndrew Geissler     }
4867f6d4bcfSAndrew Geissler 
487ced6e2a0SAndrew Geissler     return;
488ced6e2a0SAndrew Geissler }
489ced6e2a0SAndrew Geissler 
erase(uint32_t entryId)49099a8549eSDeepak Kodihalli void Manager::erase(uint32_t entryId)
49199a8549eSDeepak Kodihalli {
49234438968SPatrick Venture     auto entryFound = entries.find(entryId);
49334438968SPatrick Venture     if (entries.end() != entryFound)
49499a8549eSDeepak Kodihalli     {
49599c2b405SMatt Spinler         for (auto& func : Extensions::getDeleteProhibitedFunctions())
49699c2b405SMatt Spinler         {
49799c2b405SMatt Spinler             try
49899c2b405SMatt Spinler             {
49999c2b405SMatt Spinler                 bool prohibited = false;
50099c2b405SMatt Spinler                 func(entryId, prohibited);
50199c2b405SMatt Spinler                 if (prohibited)
50299c2b405SMatt Spinler                 {
50399c2b405SMatt Spinler                     // Future work remains to throw an error here.
50499c2b405SMatt Spinler                     return;
50599c2b405SMatt Spinler                 }
50699c2b405SMatt Spinler             }
50766491c61SPatrick Williams             catch (const std::exception& e)
50899c2b405SMatt Spinler             {
5095f285c53SPatrick Williams                 lg2::error("An extension's deleteProhibited function threw an "
5105f285c53SPatrick Williams                            "exception: {ERROR}",
5115f285c53SPatrick Williams                            "ERROR", e);
51299c2b405SMatt Spinler             }
51399c2b405SMatt Spinler         }
51499c2b405SMatt Spinler 
5153388799dSDeepak Kodihalli         // Delete the persistent representation of this error.
5163388799dSDeepak Kodihalli         fs::path errorPath(ERRLOG_PERSIST_PATH);
5178959efcbSMarri Devender Rao         errorPath /= std::to_string(entryId);
5183388799dSDeepak Kodihalli         fs::remove(errorPath);
519e4b0b771SNagaraju Goruganti 
520f18bf836SPatrick Venture         auto removeId = [](std::list<uint32_t>& ids, uint32_t id) {
521e4b0b771SNagaraju Goruganti             auto it = std::find(ids.begin(), ids.end(), id);
522e4b0b771SNagaraju Goruganti             if (it != ids.end())
523e4b0b771SNagaraju Goruganti             {
524e4b0b771SNagaraju Goruganti                 ids.erase(it);
525e4b0b771SNagaraju Goruganti             }
526e4b0b771SNagaraju Goruganti         };
52734438968SPatrick Venture         if (entryFound->second->severity() >= Entry::sevLowerLimit)
528f8a5a797SNagaraju Goruganti         {
529e4b0b771SNagaraju Goruganti             removeId(infoErrors, entryId);
530f8a5a797SNagaraju Goruganti         }
531e4b0b771SNagaraju Goruganti         else
532e4b0b771SNagaraju Goruganti         {
533e4b0b771SNagaraju Goruganti             removeId(realErrors, entryId);
534f8a5a797SNagaraju Goruganti         }
53534438968SPatrick Venture         entries.erase(entryFound);
53699c2b405SMatt Spinler 
537ced6e2a0SAndrew Geissler         checkAndRemoveBlockingError(entryId);
538ced6e2a0SAndrew Geissler 
53999c2b405SMatt Spinler         for (auto& remove : Extensions::getDeleteFunctions())
54099c2b405SMatt Spinler         {
54199c2b405SMatt Spinler             try
54299c2b405SMatt Spinler             {
54399c2b405SMatt Spinler                 remove(entryId);
54499c2b405SMatt Spinler             }
54566491c61SPatrick Williams             catch (const std::exception& e)
54699c2b405SMatt Spinler             {
5475f285c53SPatrick Williams                 lg2::error("An extension's delete function threw an exception: "
5485f285c53SPatrick Williams                            "{ERROR}",
5495f285c53SPatrick Williams                            "ERROR", e);
55099c2b405SMatt Spinler             }
55199c2b405SMatt Spinler         }
55299a8549eSDeepak Kodihalli     }
5538959efcbSMarri Devender Rao     else
5548959efcbSMarri Devender Rao     {
5555f285c53SPatrick Williams         lg2::error("Invalid entry ID ({ID}) to delete", "ID", entryId);
5568959efcbSMarri Devender Rao     }
55799a8549eSDeepak Kodihalli }
55899a8549eSDeepak Kodihalli 
restore()55972654f10SDeepak Kodihalli void Manager::restore()
56072654f10SDeepak Kodihalli {
561f18bf836SPatrick Venture     auto sanity = [](const auto& id, const auto& restoredId) {
562979ed1ccSMarri Devender Rao         return id == restoredId;
563979ed1ccSMarri Devender Rao     };
56472654f10SDeepak Kodihalli 
56572654f10SDeepak Kodihalli     fs::path dir(ERRLOG_PERSIST_PATH);
56672654f10SDeepak Kodihalli     if (!fs::exists(dir) || fs::is_empty(dir))
56772654f10SDeepak Kodihalli     {
56872654f10SDeepak Kodihalli         return;
56972654f10SDeepak Kodihalli     }
57072654f10SDeepak Kodihalli 
57172654f10SDeepak Kodihalli     for (auto& file : fs::directory_iterator(dir))
57272654f10SDeepak Kodihalli     {
57372654f10SDeepak Kodihalli         auto id = file.path().filename().c_str();
57472654f10SDeepak Kodihalli         auto idNum = std::stol(id);
57572654f10SDeepak Kodihalli         auto e = std::make_unique<Entry>(
576f18bf836SPatrick Venture             busLog, std::string(OBJ_ENTRY) + '/' + id, idNum, *this);
57772654f10SDeepak Kodihalli         if (deserialize(file.path(), *e))
57872654f10SDeepak Kodihalli         {
579979ed1ccSMarri Devender Rao             // validate the restored error entry id
580979ed1ccSMarri Devender Rao             if (sanity(static_cast<uint32_t>(idNum), e->id()))
581979ed1ccSMarri Devender Rao             {
582ef952af2SMatt Spinler                 e->path(file.path(), true);
583f8a5a797SNagaraju Goruganti                 if (e->severity() >= Entry::sevLowerLimit)
584f8a5a797SNagaraju Goruganti                 {
585f8a5a797SNagaraju Goruganti                     infoErrors.push_back(idNum);
586f8a5a797SNagaraju Goruganti                 }
587e4b0b771SNagaraju Goruganti                 else
588e4b0b771SNagaraju Goruganti                 {
589e4b0b771SNagaraju Goruganti                     realErrors.push_back(idNum);
590e4b0b771SNagaraju Goruganti                 }
591979ed1ccSMarri Devender Rao 
59272654f10SDeepak Kodihalli                 entries.insert(std::make_pair(idNum, std::move(e)));
59372654f10SDeepak Kodihalli             }
594979ed1ccSMarri Devender Rao             else
595979ed1ccSMarri Devender Rao             {
5965f285c53SPatrick Williams                 lg2::error(
597979ed1ccSMarri Devender Rao                     "Failed in sanity check while restoring error entry. "
5985f285c53SPatrick Williams                     "Ignoring error entry {ID_NUM}/{ENTRY_ID}.",
5995f285c53SPatrick Williams                     "ID_NUM", idNum, "ENTRY_ID", e->id());
600979ed1ccSMarri Devender Rao             }
601979ed1ccSMarri Devender Rao         }
60272654f10SDeepak Kodihalli     }
60372654f10SDeepak Kodihalli 
60401bf5c4bSLei YU     if (!entries.empty())
60537af9bacSVishwanatha Subbanna     {
60601bf5c4bSLei YU         entryId = entries.rbegin()->first;
60772654f10SDeepak Kodihalli     }
60837af9bacSVishwanatha Subbanna }
60972654f10SDeepak Kodihalli 
readFWVersion()6101275bd13SMatt Spinler std::string Manager::readFWVersion()
6111275bd13SMatt Spinler {
612f61f2921SMatt Spinler     auto version = util::getOSReleaseValue("VERSION_ID");
6131275bd13SMatt Spinler 
614f61f2921SMatt Spinler     if (!version)
6151275bd13SMatt Spinler     {
6165f285c53SPatrick Williams         lg2::error("Unable to read BMC firmware version");
6171275bd13SMatt Spinler     }
6181275bd13SMatt Spinler 
619f61f2921SMatt Spinler     return version.value_or("");
6201275bd13SMatt Spinler }
6211275bd13SMatt Spinler 
create(const std::string & message,Entry::Level severity,const std::map<std::string,std::string> & additionalData,const FFDCEntries & ffdc)6223fb83b37SMatt Spinler void Manager::create(const std::string& message, Entry::Level severity,
623c64b7122SMatt Spinler                      const std::map<std::string, std::string>& additionalData,
624c64b7122SMatt Spinler                      const FFDCEntries& ffdc)
625c64b7122SMatt Spinler {
626c64b7122SMatt Spinler     // Convert the map into a vector of "key=value" strings
627c64b7122SMatt Spinler     std::vector<std::string> ad;
628c64b7122SMatt Spinler     metadata::associations::combine(additionalData, ad);
629c64b7122SMatt Spinler 
630c64b7122SMatt Spinler     createEntry(message, severity, ad, ffdc);
631c64b7122SMatt Spinler }
632c64b7122SMatt Spinler 
63305aae8bcSNagaraju Goruganti } // namespace internal
6348f7941edSAdriana Kobylak } // namespace logging
635f18bf836SPatrick Venture } // namespace phosphor
636