blob: bd36d662d2978c5c2b503ae1f1cb654701f3ac05 [file] [log] [blame]
Adriana Kobylakd311bc82016-10-16 09:54:40 -05001#include <fstream>
2#include <iostream>
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -06003#include <chrono>
Adriana Kobylakd311bc82016-10-16 09:54:40 -05004#include <cstdio>
Adriana Kobylakfbe88722017-02-22 16:49:59 -06005#include <set>
Adriana Kobylakd311bc82016-10-16 09:54:40 -05006#include <string>
7#include <vector>
Adriana Kobylak1db1bd32016-10-10 11:39:20 -05008#include <sdbusplus/vtable.hpp>
9#include <systemd/sd-bus.h>
Adriana Kobylakd311bc82016-10-16 09:54:40 -050010#include <systemd/sd-journal.h>
Adriana Kobylak4ea7f312017-01-10 12:52:34 -060011#include "config.h"
12#include "elog_entry.hpp"
Saqib Khan2bb15192017-02-13 13:19:55 -060013#include <phosphor-logging/log.hpp>
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060014#include "log_manager.hpp"
Deepak Kodihallia87c1572017-02-28 07:40:34 -060015#include "elog_meta.hpp"
Deepak Kodihalli72654f12017-06-12 04:33:29 -050016#include "elog_serialize.hpp"
Deepak Kodihallia87c1572017-02-28 07:40:34 -060017
18using namespace phosphor::logging;
19extern const std::map<metadata::Metadata,
20 std::function<metadata::associations::Type>> meta;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050021
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060022namespace phosphor
23{
24namespace logging
25{
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -050026namespace internal
27{
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060028void Manager::commit(uint64_t transactionId, std::string errMsg)
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050029{
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050030 auto reqLevel = level::ERR; // Default to ERR
31 size_t realErrCnt = entries.size() - infoErrors.size();
32 auto levelmap = g_errLevelMap.find(errMsg);
33
34 if (levelmap != g_errLevelMap.end())
Marri Devender Rao7656fba2017-08-06 05:42:52 -050035 {
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050036 reqLevel = levelmap->second;
Marri Devender Rao7656fba2017-08-06 05:42:52 -050037 }
38
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050039 if (static_cast<Entry::Level>(reqLevel) < Entry::sevLowerLimit)
40 {
41 if (capped)
42 {
43 return;
44 }
45 if (realErrCnt >= ERROR_CAP)
46 {
47 log<level::ERR>("Reached error cap, Ignoring error",
48 entry("SIZE=%d", realErrCnt),
49 entry("ERROR_CAP=%d", ERROR_CAP));
50 capped = true;
51 return;
52 }
53 }
54 else
55 {
56 if (infoErrors.size() >= ERROR_INFO_CAP)
57 {
58 erase(infoErrors.front());
59 }
60 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -060061 constexpr const auto transactionIdVar = "TRANSACTION_ID";
Adriana Kobylak27c87d92017-03-06 12:45:09 -060062 // Length of 'TRANSACTION_ID' string.
Adriana Kobylak67218992017-02-28 12:53:37 -060063 constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
Adriana Kobylak27c87d92017-03-06 12:45:09 -060064 // Length of 'TRANSACTION_ID=' string.
65 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050066
Adriana Kobylakcfd9a7d2017-06-07 11:57:31 -050067 // Flush all the pending log messages into the journal via Synchronize
68 constexpr auto JOURNAL_BUSNAME = "org.freedesktop.journal1";
69 constexpr auto JOURNAL_PATH = "/org/freedesktop/journal1";
70 constexpr auto JOURNAL_INTERFACE = "org.freedesktop.journal1";
71 auto bus = sdbusplus::bus::new_default();
72 auto method = bus.new_method_call(JOURNAL_BUSNAME, JOURNAL_PATH,
73 JOURNAL_INTERFACE, "Synchronize");
74 bus.call_noreply(method);
75
Adriana Kobylakd311bc82016-10-16 09:54:40 -050076 sd_journal *j = nullptr;
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060077 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
Adriana Kobylakd311bc82016-10-16 09:54:40 -050078 if (rc < 0)
79 {
80 logging::log<logging::level::ERR>("Failed to open journal",
81 logging::entry("DESCRIPTION=%s", strerror(-rc)));
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060082 return;
Adriana Kobylakd311bc82016-10-16 09:54:40 -050083 }
84
Adriana Kobylak7298dc22017-01-24 12:21:50 -060085 std::string transactionIdStr = std::to_string(transactionId);
Adriana Kobylakd722b3a2017-02-28 12:10:44 -060086 std::set<std::string> metalist;
87 auto metamap = g_errMetaMap.find(errMsg);
88 if (metamap != g_errMetaMap.end())
89 {
90 metalist.insert(metamap->second.begin(), metamap->second.end());
91 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -060092
Jayanth Othayothdb18ebe2017-09-04 00:48:02 -050093 //Add _PID field information in AdditionalData.
94 metalist.insert("_PID");
95
Tom Joseph7a33ee42017-07-25 00:04:20 +053096 std::vector<std::string> additionalData;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060097
Adriana Kobylakfbe88722017-02-22 16:49:59 -060098 // Read the journal from the end to get the most recent entry first.
99 // The result from the sd_journal_get_data() is of the form VARIABLE=value.
100 SD_JOURNAL_FOREACH_BACKWARDS(j)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600101 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600102 const char *data = nullptr;
103 size_t length = 0;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600104
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600105 // Look for the transaction id metadata variable
106 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
107 &length);
108 if (rc < 0)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600109 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600110 // This journal entry does not have the TRANSACTION_ID
111 // metadata variable.
112 continue;
113 }
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600114
Adriana Kobylak27c87d92017-03-06 12:45:09 -0600115 // journald does not guarantee that sd_journal_get_data() returns NULL
116 // terminated strings, so need to specify the size to use to compare,
117 // use the returned length instead of anything that relies on NULL
118 // terminators like strlen().
119 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
120 // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
121 // 'data + transactionIdVarOffset' will be in the form of '1234'.
122 // 'length - transactionIdVarOffset' will be the length of '1234'.
123 if ((length <= (transactionIdVarOffset)) ||
124 (transactionIdStr.compare(0,
125 transactionIdStr.size(),
126 data + transactionIdVarOffset,
127 length - transactionIdVarOffset) != 0))
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600128 {
129 // The value of the TRANSACTION_ID metadata is not the requested
130 // transaction id number.
131 continue;
132 }
133
134 // Search for all metadata variables in the current journal entry.
135 for (auto i = metalist.cbegin(); i != metalist.cend();)
136 {
137 rc = sd_journal_get_data(j, (*i).c_str(),
138 (const void **)&data, &length);
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600139 if (rc < 0)
140 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600141 // Metadata variable not found, check next metadata variable.
142 i++;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600143 continue;
144 }
145
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600146 // Metadata variable found, save it and remove it from the set.
147 additionalData.emplace_back(data, length);
148 i = metalist.erase(i);
149 }
150 if (metalist.empty())
151 {
152 // All metadata variables found, break out of journal loop.
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600153 break;
154 }
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600155 }
156 if (!metalist.empty())
157 {
158 // Not all the metadata variables were found in the journal.
159 for (auto& metaVarStr : metalist)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600160 {
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600161 logging::log<logging::level::INFO>("Failed to find metadata",
162 logging::entry("META_FIELD=%s", metaVarStr.c_str()));
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600163 }
164 }
165
Adriana Kobylakd311bc82016-10-16 09:54:40 -0500166 sd_journal_close(j);
167
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600168 // Create error Entry dbus object
169 entryId++;
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500170 if (static_cast<Entry::Level>(reqLevel) >= Entry::sevLowerLimit)
171 {
172 infoErrors.push_back(entryId);
173 }
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600174 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
175 std::chrono::system_clock::now().time_since_epoch()).count();
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600176 auto objPath = std::string(OBJ_ENTRY) + '/' +
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600177 std::to_string(entryId);
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600178
Deepak Kodihalli35b46372017-02-27 04:58:18 -0600179 AssociationList objects {};
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600180 processMetadata(errMsg, additionalData, objects);
181
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500182 auto e = std::make_unique<Entry>(
183 busLog,
184 objPath,
185 entryId,
186 ms, // Milliseconds since 1970
187 static_cast<Entry::Level>(reqLevel),
188 std::move(errMsg),
189 std::move(additionalData),
190 std::move(objects),
191 *this);
192 serialize(*e);
193 entries.insert(std::make_pair(entryId, std::move(e)));
Adriana Kobylak1db1bd32016-10-10 11:39:20 -0500194}
195
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600196void Manager::processMetadata(const std::string& errorName,
197 const std::vector<std::string>& additionalData,
198 AssociationList& objects) const
199{
200 // additionalData is a list of "metadata=value"
201 constexpr auto separator = '=';
202 for(const auto& entry: additionalData)
203 {
204 auto found = entry.find(separator);
205 if(std::string::npos != found)
206 {
207 auto metadata = entry.substr(0, found);
208 auto iter = meta.find(metadata);
209 if(meta.end() != iter)
210 {
211 (iter->second)(metadata, additionalData, objects);
212 }
213 }
214 }
215}
216
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500217void Manager::erase(uint32_t entryId)
218{
219 auto entry = entries.find(entryId);
Deepak Kodihalli33887992017-06-13 07:06:49 -0500220 auto id = entry->second->id();
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500221 if(entries.end() != entry)
222 {
Deepak Kodihalli33887992017-06-13 07:06:49 -0500223 // Delete the persistent representation of this error.
224 fs::path errorPath(ERRLOG_PERSIST_PATH);
225 errorPath /= std::to_string(id);
226 fs::remove(errorPath);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500227 if (entry->second->severity() >= Entry::sevLowerLimit)
228 {
229 auto it = std::find(infoErrors.begin(), infoErrors.end(), entryId);
230 if (it != infoErrors.end())
231 {
232 infoErrors.erase(it);
233 }
234 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500235 entries.erase(entry);
236 }
Marri Devender Rao7656fba2017-08-06 05:42:52 -0500237
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500238 size_t realErrCnt = entries.size() - infoErrors.size();
239
240 if (realErrCnt < ERROR_CAP)
Marri Devender Rao7656fba2017-08-06 05:42:52 -0500241 {
242 capped = false;
243 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500244}
245
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500246void Manager::restore()
247{
248 std::vector<uint32_t> errorIds;
249
250 fs::path dir(ERRLOG_PERSIST_PATH);
251 if (!fs::exists(dir) || fs::is_empty(dir))
252 {
253 return;
254 }
255
256 for(auto& file: fs::directory_iterator(dir))
257 {
258 auto id = file.path().filename().c_str();
259 auto idNum = std::stol(id);
260 auto e = std::make_unique<Entry>(
261 busLog,
262 std::string(OBJ_ENTRY) + '/' + id,
263 idNum,
264 *this);
265 if (deserialize(file.path(), *e))
266 {
267 e->emit_object_added();
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500268 if (e->severity() >= Entry::sevLowerLimit)
269 {
270 infoErrors.push_back(idNum);
271 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500272 entries.insert(std::make_pair(idNum, std::move(e)));
273 errorIds.push_back(idNum);
274 }
275 }
276
Vishwanatha Subbanna37af9ba2017-09-28 16:33:53 +0530277 if (!errorIds.empty())
278 {
279 entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
280 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500281}
282
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -0500283} // namespace internal
Adriana Kobylak8f7941e2016-11-14 14:46:23 -0600284} // namespace logging
285} // namepsace phosphor