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"
8*99c2b405SMatt Spinler #include "extensions.hpp"
9f18bf836SPatrick Venture 
105f4247f0SAdriana Kobylak #include <poll.h>
115f4247f0SAdriana Kobylak #include <sys/inotify.h>
121db1bd35SAdriana Kobylak #include <systemd/sd-bus.h>
13d311bc8dSAdriana Kobylak #include <systemd/sd-journal.h>
145f4247f0SAdriana Kobylak #include <unistd.h>
15f18bf836SPatrick Venture 
16*99c2b405SMatt Spinler #include <cassert>
17f18bf836SPatrick Venture #include <chrono>
18f18bf836SPatrick Venture #include <cstdio>
1930047bf9SPatrick Venture #include <cstring>
20f18bf836SPatrick Venture #include <fstream>
2130047bf9SPatrick Venture #include <functional>
22f18bf836SPatrick Venture #include <future>
23f18bf836SPatrick Venture #include <iostream>
2430047bf9SPatrick Venture #include <map>
252bb15195SSaqib Khan #include <phosphor-logging/log.hpp>
26f18bf836SPatrick Venture #include <sdbusplus/vtable.hpp>
27f18bf836SPatrick Venture #include <set>
28f18bf836SPatrick Venture #include <string>
29f18bf836SPatrick Venture #include <vector>
30a87c157cSDeepak Kodihalli 
31a87c157cSDeepak Kodihalli using namespace phosphor::logging;
325f4247f0SAdriana Kobylak using namespace std::chrono;
33a87c157cSDeepak Kodihalli extern const std::map<metadata::Metadata,
34f18bf836SPatrick Venture                       std::function<metadata::associations::Type>>
35f18bf836SPatrick Venture     meta;
361db1bd35SAdriana Kobylak 
378f7941edSAdriana Kobylak namespace phosphor
388f7941edSAdriana Kobylak {
398f7941edSAdriana Kobylak namespace logging
408f7941edSAdriana Kobylak {
4105aae8bcSNagaraju Goruganti namespace internal
4205aae8bcSNagaraju Goruganti {
436fd9dc48SDeepak Kodihalli inline auto getLevel(const std::string& errMsg)
446fd9dc48SDeepak Kodihalli {
456fd9dc48SDeepak Kodihalli     auto reqLevel = Entry::Level::Error; // Default to Error
466fd9dc48SDeepak Kodihalli 
476fd9dc48SDeepak Kodihalli     auto levelmap = g_errLevelMap.find(errMsg);
48f8a5a797SNagaraju Goruganti     if (levelmap != g_errLevelMap.end())
49f8a5a797SNagaraju Goruganti     {
506fd9dc48SDeepak Kodihalli         reqLevel = static_cast<Entry::Level>(levelmap->second);
51f8a5a797SNagaraju Goruganti     }
52f8a5a797SNagaraju Goruganti 
536fd9dc48SDeepak Kodihalli     return reqLevel;
546fd9dc48SDeepak Kodihalli }
556fd9dc48SDeepak Kodihalli 
56477b731aSNagaraju Goruganti int Manager::getRealErrSize()
57477b731aSNagaraju Goruganti {
58477b731aSNagaraju Goruganti     return realErrors.size();
59477b731aSNagaraju Goruganti }
60477b731aSNagaraju Goruganti 
61477b731aSNagaraju Goruganti int Manager::getInfoErrSize()
62477b731aSNagaraju Goruganti {
63477b731aSNagaraju Goruganti     return infoErrors.size();
64477b731aSNagaraju Goruganti }
65477b731aSNagaraju Goruganti 
666fd9dc48SDeepak Kodihalli void Manager::commit(uint64_t transactionId, std::string errMsg)
676fd9dc48SDeepak Kodihalli {
686fd9dc48SDeepak Kodihalli     auto level = getLevel(errMsg);
696fd9dc48SDeepak Kodihalli     _commit(transactionId, std::move(errMsg), level);
706fd9dc48SDeepak Kodihalli }
716fd9dc48SDeepak Kodihalli 
726fd9dc48SDeepak Kodihalli void Manager::commitWithLvl(uint64_t transactionId, std::string errMsg,
736fd9dc48SDeepak Kodihalli                             uint32_t errLvl)
746fd9dc48SDeepak Kodihalli {
756fd9dc48SDeepak Kodihalli     _commit(transactionId, std::move(errMsg),
766fd9dc48SDeepak Kodihalli             static_cast<Entry::Level>(errLvl));
776fd9dc48SDeepak Kodihalli }
786fd9dc48SDeepak Kodihalli 
796fd9dc48SDeepak Kodihalli void Manager::_commit(uint64_t transactionId, std::string&& errMsg,
806fd9dc48SDeepak Kodihalli                       Entry::Level errLvl)
816fd9dc48SDeepak Kodihalli {
82*99c2b405SMatt Spinler     if (!Extensions::disableDefaultLogCaps())
83*99c2b405SMatt Spinler     {
846fd9dc48SDeepak Kodihalli         if (errLvl < Entry::sevLowerLimit)
85f8a5a797SNagaraju Goruganti         {
86e4b0b771SNagaraju Goruganti             if (realErrors.size() >= ERROR_CAP)
877656fba3SMarri Devender Rao             {
88e4b0b771SNagaraju Goruganti                 erase(realErrors.front());
897656fba3SMarri Devender Rao             }
90f8a5a797SNagaraju Goruganti         }
91f8a5a797SNagaraju Goruganti         else
92f8a5a797SNagaraju Goruganti         {
93f8a5a797SNagaraju Goruganti             if (infoErrors.size() >= ERROR_INFO_CAP)
94f8a5a797SNagaraju Goruganti             {
95f8a5a797SNagaraju Goruganti                 erase(infoErrors.front());
96f8a5a797SNagaraju Goruganti             }
97f8a5a797SNagaraju Goruganti         }
98*99c2b405SMatt Spinler     }
99*99c2b405SMatt Spinler 
1007298dc23SAdriana Kobylak     constexpr const auto transactionIdVar = "TRANSACTION_ID";
10127c87d92SAdriana Kobylak     // Length of 'TRANSACTION_ID' string.
10230047bf9SPatrick Venture     constexpr const auto transactionIdVarSize = std::strlen(transactionIdVar);
10327c87d92SAdriana Kobylak     // Length of 'TRANSACTION_ID=' string.
10427c87d92SAdriana Kobylak     constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
1051db1bd35SAdriana Kobylak 
1065f4247f0SAdriana Kobylak     // Flush all the pending log messages into the journal
1075f4247f0SAdriana Kobylak     journalSync();
108cfd9a7ddSAdriana Kobylak 
109d311bc8dSAdriana Kobylak     sd_journal* j = nullptr;
1108f7941edSAdriana Kobylak     int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
111d311bc8dSAdriana Kobylak     if (rc < 0)
112d311bc8dSAdriana Kobylak     {
113f18bf836SPatrick Venture         logging::log<logging::level::ERR>(
114f18bf836SPatrick Venture             "Failed to open journal",
115d311bc8dSAdriana Kobylak             logging::entry("DESCRIPTION=%s", strerror(-rc)));
1168f7941edSAdriana Kobylak         return;
117d311bc8dSAdriana Kobylak     }
118d311bc8dSAdriana Kobylak 
1197298dc23SAdriana Kobylak     std::string transactionIdStr = std::to_string(transactionId);
120d722b3aaSAdriana Kobylak     std::set<std::string> metalist;
121d722b3aaSAdriana Kobylak     auto metamap = g_errMetaMap.find(errMsg);
122d722b3aaSAdriana Kobylak     if (metamap != g_errMetaMap.end())
123d722b3aaSAdriana Kobylak     {
124d722b3aaSAdriana Kobylak         metalist.insert(metamap->second.begin(), metamap->second.end());
125d722b3aaSAdriana Kobylak     }
1267298dc23SAdriana Kobylak 
127db18ebe0SJayanth Othayoth     // Add _PID field information in AdditionalData.
128db18ebe0SJayanth Othayoth     metalist.insert("_PID");
129db18ebe0SJayanth Othayoth 
1307a33ee40STom Joseph     std::vector<std::string> additionalData;
1319aa7d789SAdriana Kobylak 
132d311bc8dSAdriana Kobylak     // Read the journal from the end to get the most recent entry first.
133d311bc8dSAdriana Kobylak     // The result from the sd_journal_get_data() is of the form VARIABLE=value.
134d311bc8dSAdriana Kobylak     SD_JOURNAL_FOREACH_BACKWARDS(j)
135d311bc8dSAdriana Kobylak     {
136d311bc8dSAdriana Kobylak         const char* data = nullptr;
137d311bc8dSAdriana Kobylak         size_t length = 0;
138d311bc8dSAdriana Kobylak 
1397298dc23SAdriana Kobylak         // Look for the transaction id metadata variable
1407298dc23SAdriana Kobylak         rc = sd_journal_get_data(j, transactionIdVar, (const void**)&data,
1417298dc23SAdriana Kobylak                                  &length);
142d311bc8dSAdriana Kobylak         if (rc < 0)
143d311bc8dSAdriana Kobylak         {
144fbe8872eSAdriana Kobylak             // This journal entry does not have the TRANSACTION_ID
145fbe8872eSAdriana Kobylak             // metadata variable.
146d311bc8dSAdriana Kobylak             continue;
147d311bc8dSAdriana Kobylak         }
1487298dc23SAdriana Kobylak 
14927c87d92SAdriana Kobylak         // journald does not guarantee that sd_journal_get_data() returns NULL
15027c87d92SAdriana Kobylak         // terminated strings, so need to specify the size to use to compare,
15127c87d92SAdriana Kobylak         // use the returned length instead of anything that relies on NULL
15227c87d92SAdriana Kobylak         // terminators like strlen().
15327c87d92SAdriana Kobylak         // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
15427c87d92SAdriana Kobylak         // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
15527c87d92SAdriana Kobylak         // 'data + transactionIdVarOffset' will be in the form of '1234'.
15627c87d92SAdriana Kobylak         // 'length - transactionIdVarOffset' will be the length of '1234'.
15727c87d92SAdriana Kobylak         if ((length <= (transactionIdVarOffset)) ||
158f18bf836SPatrick Venture             (transactionIdStr.compare(0, transactionIdStr.size(),
15927c87d92SAdriana Kobylak                                       data + transactionIdVarOffset,
16027c87d92SAdriana Kobylak                                       length - transactionIdVarOffset) != 0))
161d311bc8dSAdriana Kobylak         {
162fbe8872eSAdriana Kobylak             // The value of the TRANSACTION_ID metadata is not the requested
163fbe8872eSAdriana Kobylak             // transaction id number.
164d311bc8dSAdriana Kobylak             continue;
165d311bc8dSAdriana Kobylak         }
166d311bc8dSAdriana Kobylak 
167fbe8872eSAdriana Kobylak         // Search for all metadata variables in the current journal entry.
168fbe8872eSAdriana Kobylak         for (auto i = metalist.cbegin(); i != metalist.cend();)
169fbe8872eSAdriana Kobylak         {
170f18bf836SPatrick Venture             rc = sd_journal_get_data(j, (*i).c_str(), (const void**)&data,
171f18bf836SPatrick Venture                                      &length);
172fbe8872eSAdriana Kobylak             if (rc < 0)
173fbe8872eSAdriana Kobylak             {
174fbe8872eSAdriana Kobylak                 // Metadata variable not found, check next metadata variable.
175fbe8872eSAdriana Kobylak                 i++;
176fbe8872eSAdriana Kobylak                 continue;
177fbe8872eSAdriana Kobylak             }
178fbe8872eSAdriana Kobylak 
179fbe8872eSAdriana Kobylak             // Metadata variable found, save it and remove it from the set.
180fbe8872eSAdriana Kobylak             additionalData.emplace_back(data, length);
181fbe8872eSAdriana Kobylak             i = metalist.erase(i);
182fbe8872eSAdriana Kobylak         }
183fbe8872eSAdriana Kobylak         if (metalist.empty())
184fbe8872eSAdriana Kobylak         {
185fbe8872eSAdriana Kobylak             // All metadata variables found, break out of journal loop.
1869aa7d789SAdriana Kobylak             break;
1879aa7d789SAdriana Kobylak         }
188fbe8872eSAdriana Kobylak     }
189fbe8872eSAdriana Kobylak     if (!metalist.empty())
190d311bc8dSAdriana Kobylak     {
191fbe8872eSAdriana Kobylak         // Not all the metadata variables were found in the journal.
192fbe8872eSAdriana Kobylak         for (auto& metaVarStr : metalist)
193fbe8872eSAdriana Kobylak         {
194f18bf836SPatrick Venture             logging::log<logging::level::INFO>(
195f18bf836SPatrick Venture                 "Failed to find metadata",
1967298dc23SAdriana Kobylak                 logging::entry("META_FIELD=%s", metaVarStr.c_str()));
197d311bc8dSAdriana Kobylak         }
198d311bc8dSAdriana Kobylak     }
199d311bc8dSAdriana Kobylak 
200d311bc8dSAdriana Kobylak     sd_journal_close(j);
201d311bc8dSAdriana Kobylak 
2024ea7f312SAdriana Kobylak     // Create error Entry dbus object
2034ea7f312SAdriana Kobylak     entryId++;
2046fd9dc48SDeepak Kodihalli     if (errLvl >= Entry::sevLowerLimit)
205f8a5a797SNagaraju Goruganti     {
206f8a5a797SNagaraju Goruganti         infoErrors.push_back(entryId);
207f8a5a797SNagaraju Goruganti     }
208e4b0b771SNagaraju Goruganti     else
209e4b0b771SNagaraju Goruganti     {
210e4b0b771SNagaraju Goruganti         realErrors.push_back(entryId);
211e4b0b771SNagaraju Goruganti     }
212c5f0bbd9SAdriana Kobylak     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
213f18bf836SPatrick Venture                   std::chrono::system_clock::now().time_since_epoch())
214f18bf836SPatrick Venture                   .count();
215f18bf836SPatrick Venture     auto objPath = std::string(OBJ_ENTRY) + '/' + std::to_string(entryId);
216a87c157cSDeepak Kodihalli 
21735b46379SDeepak Kodihalli     AssociationList objects{};
218a87c157cSDeepak Kodihalli     processMetadata(errMsg, additionalData, objects);
219a87c157cSDeepak Kodihalli 
220f18bf836SPatrick Venture     auto e = std::make_unique<Entry>(busLog, objPath, entryId,
221c5f0bbd9SAdriana Kobylak                                      ms, // Milliseconds since 1970
222f18bf836SPatrick Venture                                      errLvl, std::move(errMsg),
22335b46379SDeepak Kodihalli                                      std::move(additionalData),
224f18bf836SPatrick Venture                                      std::move(objects), fwVersion, *this);
22572654f10SDeepak Kodihalli     serialize(*e);
226*99c2b405SMatt Spinler 
227*99c2b405SMatt Spinler     doExtensionLogCreate(*e);
228*99c2b405SMatt Spinler 
22972654f10SDeepak Kodihalli     entries.insert(std::make_pair(entryId, std::move(e)));
2301db1bd35SAdriana Kobylak }
2311db1bd35SAdriana Kobylak 
232*99c2b405SMatt Spinler void Manager::doExtensionLogCreate(const Entry& entry)
233*99c2b405SMatt Spinler {
234*99c2b405SMatt Spinler     // Make the association <endpointpath>/<endpointtype> paths
235*99c2b405SMatt Spinler     std::vector<std::string> assocs;
236*99c2b405SMatt Spinler     for (const auto& [forwardType, reverseType, endpoint] :
237*99c2b405SMatt Spinler          entry.associations())
238*99c2b405SMatt Spinler     {
239*99c2b405SMatt Spinler         std::string e{endpoint};
240*99c2b405SMatt Spinler         e += '/' + reverseType;
241*99c2b405SMatt Spinler         assocs.push_back(e);
242*99c2b405SMatt Spinler     }
243*99c2b405SMatt Spinler 
244*99c2b405SMatt Spinler     for (auto& create : Extensions::getCreateFunctions())
245*99c2b405SMatt Spinler     {
246*99c2b405SMatt Spinler         try
247*99c2b405SMatt Spinler         {
248*99c2b405SMatt Spinler             create(entry.message(), entry.id(), entry.timestamp(),
249*99c2b405SMatt Spinler                    entry.severity(), entry.additionalData(), assocs);
250*99c2b405SMatt Spinler         }
251*99c2b405SMatt Spinler         catch (std::exception& e)
252*99c2b405SMatt Spinler         {
253*99c2b405SMatt Spinler             log<level::ERR>("An extension's create function threw an exception",
254*99c2b405SMatt Spinler                             phosphor::logging::entry("ERROR=%s", e.what()));
255*99c2b405SMatt Spinler         }
256*99c2b405SMatt Spinler     }
257*99c2b405SMatt Spinler }
258*99c2b405SMatt Spinler 
259a87c157cSDeepak Kodihalli void Manager::processMetadata(const std::string& errorName,
260a87c157cSDeepak Kodihalli                               const std::vector<std::string>& additionalData,
261a87c157cSDeepak Kodihalli                               AssociationList& objects) const
262a87c157cSDeepak Kodihalli {
263a87c157cSDeepak Kodihalli     // additionalData is a list of "metadata=value"
264a87c157cSDeepak Kodihalli     constexpr auto separator = '=';
26534438968SPatrick Venture     for (const auto& entryItem : additionalData)
266a87c157cSDeepak Kodihalli     {
26734438968SPatrick Venture         auto found = entryItem.find(separator);
268a87c157cSDeepak Kodihalli         if (std::string::npos != found)
269a87c157cSDeepak Kodihalli         {
27034438968SPatrick Venture             auto metadata = entryItem.substr(0, found);
271a87c157cSDeepak Kodihalli             auto iter = meta.find(metadata);
272a87c157cSDeepak Kodihalli             if (meta.end() != iter)
273a87c157cSDeepak Kodihalli             {
274a87c157cSDeepak Kodihalli                 (iter->second)(metadata, additionalData, objects);
275a87c157cSDeepak Kodihalli             }
276a87c157cSDeepak Kodihalli         }
277a87c157cSDeepak Kodihalli     }
278a87c157cSDeepak Kodihalli }
279a87c157cSDeepak Kodihalli 
28099a8549eSDeepak Kodihalli void Manager::erase(uint32_t entryId)
28199a8549eSDeepak Kodihalli {
28234438968SPatrick Venture     auto entryFound = entries.find(entryId);
28334438968SPatrick Venture     if (entries.end() != entryFound)
28499a8549eSDeepak Kodihalli     {
285*99c2b405SMatt Spinler         for (auto& func : Extensions::getDeleteProhibitedFunctions())
286*99c2b405SMatt Spinler         {
287*99c2b405SMatt Spinler             try
288*99c2b405SMatt Spinler             {
289*99c2b405SMatt Spinler                 bool prohibited = false;
290*99c2b405SMatt Spinler                 func(entryId, prohibited);
291*99c2b405SMatt Spinler                 if (prohibited)
292*99c2b405SMatt Spinler                 {
293*99c2b405SMatt Spinler                     // Future work remains to throw an error here.
294*99c2b405SMatt Spinler                     return;
295*99c2b405SMatt Spinler                 }
296*99c2b405SMatt Spinler             }
297*99c2b405SMatt Spinler             catch (std::exception& e)
298*99c2b405SMatt Spinler             {
299*99c2b405SMatt Spinler                 log<level::ERR>(
300*99c2b405SMatt Spinler                     "An extension's deleteProhibited function threw "
301*99c2b405SMatt Spinler                     "an exception",
302*99c2b405SMatt Spinler                     entry("ERROR=%s", e.what()));
303*99c2b405SMatt Spinler             }
304*99c2b405SMatt Spinler         }
305*99c2b405SMatt Spinler 
3063388799dSDeepak Kodihalli         // Delete the persistent representation of this error.
3073388799dSDeepak Kodihalli         fs::path errorPath(ERRLOG_PERSIST_PATH);
3088959efcbSMarri Devender Rao         errorPath /= std::to_string(entryId);
3093388799dSDeepak Kodihalli         fs::remove(errorPath);
310e4b0b771SNagaraju Goruganti 
311f18bf836SPatrick Venture         auto removeId = [](std::list<uint32_t>& ids, uint32_t id) {
312e4b0b771SNagaraju Goruganti             auto it = std::find(ids.begin(), ids.end(), id);
313e4b0b771SNagaraju Goruganti             if (it != ids.end())
314e4b0b771SNagaraju Goruganti             {
315e4b0b771SNagaraju Goruganti                 ids.erase(it);
316e4b0b771SNagaraju Goruganti             }
317e4b0b771SNagaraju Goruganti         };
31834438968SPatrick Venture         if (entryFound->second->severity() >= Entry::sevLowerLimit)
319f8a5a797SNagaraju Goruganti         {
320e4b0b771SNagaraju Goruganti             removeId(infoErrors, entryId);
321f8a5a797SNagaraju Goruganti         }
322e4b0b771SNagaraju Goruganti         else
323e4b0b771SNagaraju Goruganti         {
324e4b0b771SNagaraju Goruganti             removeId(realErrors, entryId);
325f8a5a797SNagaraju Goruganti         }
32634438968SPatrick Venture         entries.erase(entryFound);
327*99c2b405SMatt Spinler 
328*99c2b405SMatt Spinler         for (auto& remove : Extensions::getDeleteFunctions())
329*99c2b405SMatt Spinler         {
330*99c2b405SMatt Spinler             try
331*99c2b405SMatt Spinler             {
332*99c2b405SMatt Spinler                 remove(entryId);
333*99c2b405SMatt Spinler             }
334*99c2b405SMatt Spinler             catch (std::exception& e)
335*99c2b405SMatt Spinler             {
336*99c2b405SMatt Spinler                 log<level::ERR>("An extension's delete function threw an "
337*99c2b405SMatt Spinler                                 "exception",
338*99c2b405SMatt Spinler                                 entry("ERROR=%s", e.what()));
339*99c2b405SMatt Spinler             }
340*99c2b405SMatt Spinler         }
34199a8549eSDeepak Kodihalli     }
3428959efcbSMarri Devender Rao     else
3438959efcbSMarri Devender Rao     {
3448959efcbSMarri Devender Rao         logging::log<level::ERR>("Invalid entry ID to delete",
3458959efcbSMarri Devender Rao                                  logging::entry("ID=%d", entryId));
3468959efcbSMarri Devender Rao     }
34799a8549eSDeepak Kodihalli }
34899a8549eSDeepak Kodihalli 
34972654f10SDeepak Kodihalli void Manager::restore()
35072654f10SDeepak Kodihalli {
351f18bf836SPatrick Venture     auto sanity = [](const auto& id, const auto& restoredId) {
352979ed1ccSMarri Devender Rao         return id == restoredId;
353979ed1ccSMarri Devender Rao     };
35472654f10SDeepak Kodihalli     std::vector<uint32_t> errorIds;
35572654f10SDeepak Kodihalli 
35672654f10SDeepak Kodihalli     fs::path dir(ERRLOG_PERSIST_PATH);
35772654f10SDeepak Kodihalli     if (!fs::exists(dir) || fs::is_empty(dir))
35872654f10SDeepak Kodihalli     {
35972654f10SDeepak Kodihalli         return;
36072654f10SDeepak Kodihalli     }
36172654f10SDeepak Kodihalli 
36272654f10SDeepak Kodihalli     for (auto& file : fs::directory_iterator(dir))
36372654f10SDeepak Kodihalli     {
36472654f10SDeepak Kodihalli         auto id = file.path().filename().c_str();
36572654f10SDeepak Kodihalli         auto idNum = std::stol(id);
36672654f10SDeepak Kodihalli         auto e = std::make_unique<Entry>(
367f18bf836SPatrick Venture             busLog, std::string(OBJ_ENTRY) + '/' + id, idNum, *this);
36872654f10SDeepak Kodihalli         if (deserialize(file.path(), *e))
36972654f10SDeepak Kodihalli         {
370979ed1ccSMarri Devender Rao             // validate the restored error entry id
371979ed1ccSMarri Devender Rao             if (sanity(static_cast<uint32_t>(idNum), e->id()))
372979ed1ccSMarri Devender Rao             {
37372654f10SDeepak Kodihalli                 e->emit_object_added();
374f8a5a797SNagaraju Goruganti                 if (e->severity() >= Entry::sevLowerLimit)
375f8a5a797SNagaraju Goruganti                 {
376f8a5a797SNagaraju Goruganti                     infoErrors.push_back(idNum);
377f8a5a797SNagaraju Goruganti                 }
378e4b0b771SNagaraju Goruganti                 else
379e4b0b771SNagaraju Goruganti                 {
380e4b0b771SNagaraju Goruganti                     realErrors.push_back(idNum);
381e4b0b771SNagaraju Goruganti                 }
382979ed1ccSMarri Devender Rao 
38372654f10SDeepak Kodihalli                 entries.insert(std::make_pair(idNum, std::move(e)));
38472654f10SDeepak Kodihalli                 errorIds.push_back(idNum);
38572654f10SDeepak Kodihalli             }
386979ed1ccSMarri Devender Rao             else
387979ed1ccSMarri Devender Rao             {
388979ed1ccSMarri Devender Rao                 logging::log<logging::level::ERR>(
389979ed1ccSMarri Devender Rao                     "Failed in sanity check while restoring error entry. "
390979ed1ccSMarri Devender Rao                     "Ignoring error entry",
391979ed1ccSMarri Devender Rao                     logging::entry("ID_NUM=%d", idNum),
392979ed1ccSMarri Devender Rao                     logging::entry("ENTRY_ID=%d", e->id()));
393979ed1ccSMarri Devender Rao             }
394979ed1ccSMarri Devender Rao         }
39572654f10SDeepak Kodihalli     }
39672654f10SDeepak Kodihalli 
39737af9bacSVishwanatha Subbanna     if (!errorIds.empty())
39837af9bacSVishwanatha Subbanna     {
39972654f10SDeepak Kodihalli         entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
40072654f10SDeepak Kodihalli     }
40137af9bacSVishwanatha Subbanna }
40272654f10SDeepak Kodihalli 
4035f4247f0SAdriana Kobylak void Manager::journalSync()
4045f4247f0SAdriana Kobylak {
4055f4247f0SAdriana Kobylak     bool syncRequested = false;
4065f4247f0SAdriana Kobylak     auto fd = -1;
4075f4247f0SAdriana Kobylak     auto rc = -1;
4085f4247f0SAdriana Kobylak     auto wd = -1;
4095f4247f0SAdriana Kobylak     auto bus = sdbusplus::bus::new_default();
4105f4247f0SAdriana Kobylak 
4115f4247f0SAdriana Kobylak     auto start =
4125f4247f0SAdriana Kobylak         duration_cast<microseconds>(steady_clock::now().time_since_epoch())
4135f4247f0SAdriana Kobylak             .count();
4145f4247f0SAdriana Kobylak 
4155f4247f0SAdriana Kobylak     constexpr auto maxRetry = 2;
4165f4247f0SAdriana Kobylak     for (int i = 0; i < maxRetry; i++)
4175f4247f0SAdriana Kobylak     {
4185f4247f0SAdriana Kobylak         // Read timestamp from synced file
4195f4247f0SAdriana Kobylak         constexpr auto syncedPath = "/run/systemd/journal/synced";
4205f4247f0SAdriana Kobylak         std::ifstream syncedFile(syncedPath);
4215f4247f0SAdriana Kobylak         if (syncedFile.fail())
4225f4247f0SAdriana Kobylak         {
4237d111a85SAdriana Kobylak             // If the synced file doesn't exist, a sync request will create it.
4247d111a85SAdriana Kobylak             if (errno != ENOENT)
4257d111a85SAdriana Kobylak             {
4265f4247f0SAdriana Kobylak                 log<level::ERR>("Failed to open journal synced file",
4275f4247f0SAdriana Kobylak                                 entry("FILENAME=%s", syncedPath),
4285f4247f0SAdriana Kobylak                                 entry("ERRNO=%d", errno));
4295f4247f0SAdriana Kobylak                 return;
4305f4247f0SAdriana Kobylak             }
4317d111a85SAdriana Kobylak         }
4327d111a85SAdriana Kobylak         else
4337d111a85SAdriana Kobylak         {
4347d111a85SAdriana Kobylak             // Only read the synced file if it exists.
4355f4247f0SAdriana Kobylak             // See if a sync happened by now
4365f4247f0SAdriana Kobylak             std::string timestampStr;
4375f4247f0SAdriana Kobylak             std::getline(syncedFile, timestampStr);
43830047bf9SPatrick Venture             auto timestamp = std::stoll(timestampStr);
4395f4247f0SAdriana Kobylak             if (timestamp >= start)
4405f4247f0SAdriana Kobylak             {
4415f4247f0SAdriana Kobylak                 return;
4425f4247f0SAdriana Kobylak             }
4437d111a85SAdriana Kobylak         }
4445f4247f0SAdriana Kobylak 
4455f4247f0SAdriana Kobylak         // Let's ask for a sync, but only once
4465f4247f0SAdriana Kobylak         if (!syncRequested)
4475f4247f0SAdriana Kobylak         {
4485f4247f0SAdriana Kobylak             syncRequested = true;
4495f4247f0SAdriana Kobylak 
4505f4247f0SAdriana Kobylak             constexpr auto JOURNAL_UNIT = "systemd-journald.service";
4515f4247f0SAdriana Kobylak             auto signal = SIGRTMIN + 1;
4525f4247f0SAdriana Kobylak 
4535f4247f0SAdriana Kobylak             auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4545f4247f0SAdriana Kobylak                                               SYSTEMD_INTERFACE, "KillUnit");
4555f4247f0SAdriana Kobylak             method.append(JOURNAL_UNIT, "main", signal);
4565f4247f0SAdriana Kobylak             bus.call(method);
4575f4247f0SAdriana Kobylak             if (method.is_method_error())
4585f4247f0SAdriana Kobylak             {
4595f4247f0SAdriana Kobylak                 log<level::ERR>("Failed to kill journal service");
4605f4247f0SAdriana Kobylak                 return;
4615f4247f0SAdriana Kobylak             }
4625f4247f0SAdriana Kobylak             continue;
4635f4247f0SAdriana Kobylak         }
4645f4247f0SAdriana Kobylak 
4655f4247f0SAdriana Kobylak         // Let's install the inotify watch, if we didn't do that yet. This watch
4665f4247f0SAdriana Kobylak         // monitors the syncedFile for when journald updates it with a newer
4675f4247f0SAdriana Kobylak         // timestamp. This means the journal has been flushed.
4685f4247f0SAdriana Kobylak         if (fd < 0)
4695f4247f0SAdriana Kobylak         {
4705f4247f0SAdriana Kobylak             fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
4715f4247f0SAdriana Kobylak             if (fd < 0)
4725f4247f0SAdriana Kobylak             {
4735f4247f0SAdriana Kobylak                 log<level::ERR>("Failed to create inotify watch",
4745f4247f0SAdriana Kobylak                                 entry("ERRNO=%d", errno));
4755f4247f0SAdriana Kobylak                 return;
4765f4247f0SAdriana Kobylak             }
4775f4247f0SAdriana Kobylak 
4785f4247f0SAdriana Kobylak             constexpr auto JOURNAL_RUN_PATH = "/run/systemd/journal";
4795f4247f0SAdriana Kobylak             wd = inotify_add_watch(fd, JOURNAL_RUN_PATH,
4805f4247f0SAdriana Kobylak                                    IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
4815f4247f0SAdriana Kobylak             if (wd < 0)
4825f4247f0SAdriana Kobylak             {
4835f4247f0SAdriana Kobylak                 log<level::ERR>("Failed to watch journal directory",
4845f4247f0SAdriana Kobylak                                 entry("PATH=%s", JOURNAL_RUN_PATH),
4855f4247f0SAdriana Kobylak                                 entry("ERRNO=%d", errno));
4865f4247f0SAdriana Kobylak                 close(fd);
4875f4247f0SAdriana Kobylak                 return;
4885f4247f0SAdriana Kobylak             }
4895f4247f0SAdriana Kobylak             continue;
4905f4247f0SAdriana Kobylak         }
4915f4247f0SAdriana Kobylak 
4925f4247f0SAdriana Kobylak         // Let's wait until inotify reports an event
4935f4247f0SAdriana Kobylak         struct pollfd fds = {
4945f4247f0SAdriana Kobylak             .fd = fd,
4955f4247f0SAdriana Kobylak             .events = POLLIN,
4965f4247f0SAdriana Kobylak         };
4975f4247f0SAdriana Kobylak         constexpr auto pollTimeout = 5; // 5 seconds
4985f4247f0SAdriana Kobylak         rc = poll(&fds, 1, pollTimeout * 1000);
4995f4247f0SAdriana Kobylak         if (rc < 0)
5005f4247f0SAdriana Kobylak         {
5015f4247f0SAdriana Kobylak             log<level::ERR>("Failed to add event", entry("ERRNO=%d", errno),
5025f4247f0SAdriana Kobylak                             entry("ERR=%s", strerror(-rc)));
5035f4247f0SAdriana Kobylak             inotify_rm_watch(fd, wd);
5045f4247f0SAdriana Kobylak             close(fd);
5055f4247f0SAdriana Kobylak             return;
5065f4247f0SAdriana Kobylak         }
5075f4247f0SAdriana Kobylak         else if (rc == 0)
5085f4247f0SAdriana Kobylak         {
5095f4247f0SAdriana Kobylak             log<level::INFO>("Poll timeout, no new journal synced data",
5105f4247f0SAdriana Kobylak                              entry("TIMEOUT=%d", pollTimeout));
5115f4247f0SAdriana Kobylak             break;
5125f4247f0SAdriana Kobylak         }
5135f4247f0SAdriana Kobylak 
5145f4247f0SAdriana Kobylak         // Read from the specified file descriptor until there is no new data,
5155f4247f0SAdriana Kobylak         // throwing away everything read since the timestamp will be read at the
5165f4247f0SAdriana Kobylak         // beginning of the loop.
5175f4247f0SAdriana Kobylak         constexpr auto maxBytes = 64;
5185f4247f0SAdriana Kobylak         uint8_t buffer[maxBytes];
5195f4247f0SAdriana Kobylak         while (read(fd, buffer, maxBytes) > 0)
5205f4247f0SAdriana Kobylak             ;
5215f4247f0SAdriana Kobylak     }
5225f4247f0SAdriana Kobylak 
5234a029f2fSAdriana Kobylak     if (fd != -1)
5244a029f2fSAdriana Kobylak     {
5254a029f2fSAdriana Kobylak         if (wd != -1)
5265f4247f0SAdriana Kobylak         {
5275f4247f0SAdriana Kobylak             inotify_rm_watch(fd, wd);
5284a029f2fSAdriana Kobylak         }
5295f4247f0SAdriana Kobylak         close(fd);
5305f4247f0SAdriana Kobylak     }
5315f4247f0SAdriana Kobylak 
5325f4247f0SAdriana Kobylak     return;
5335f4247f0SAdriana Kobylak }
5345f4247f0SAdriana Kobylak 
5351275bd13SMatt Spinler std::string Manager::readFWVersion()
5361275bd13SMatt Spinler {
5371275bd13SMatt Spinler     std::string version;
5381275bd13SMatt Spinler     std::ifstream versionFile{BMC_VERSION_FILE};
5391275bd13SMatt Spinler     std::string line;
5401275bd13SMatt Spinler     static constexpr auto VERSION_ID = "VERSION_ID=";
5411275bd13SMatt Spinler 
5421275bd13SMatt Spinler     while (std::getline(versionFile, line))
5431275bd13SMatt Spinler     {
5441275bd13SMatt Spinler         if (line.find(VERSION_ID) != std::string::npos)
5451275bd13SMatt Spinler         {
5461275bd13SMatt Spinler             auto pos = line.find_first_of('"') + 1;
5471275bd13SMatt Spinler             version = line.substr(pos, line.find_last_of('"') - pos);
5481275bd13SMatt Spinler             break;
5491275bd13SMatt Spinler         }
5501275bd13SMatt Spinler     }
5511275bd13SMatt Spinler 
5521275bd13SMatt Spinler     if (version.empty())
5531275bd13SMatt Spinler     {
5541275bd13SMatt Spinler         log<level::ERR>("Unable to read BMC firmware version");
5551275bd13SMatt Spinler     }
5561275bd13SMatt Spinler 
5571275bd13SMatt Spinler     return version;
5581275bd13SMatt Spinler }
5591275bd13SMatt Spinler 
56005aae8bcSNagaraju Goruganti } // namespace internal
5618f7941edSAdriana Kobylak } // namespace logging
562f18bf836SPatrick Venture } // namespace phosphor
563