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