blob: 92454fe5fd2b809d1811924a677d2d929f96b0e9 [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{
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050028
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050029inline auto getLevel(const std::string& errMsg)
30{
31 auto reqLevel = Entry::Level::Error; // Default to Error
32
33 auto levelmap = g_errLevelMap.find(errMsg);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050034 if (levelmap != g_errLevelMap.end())
Marri Devender Rao7656fba2017-08-06 05:42:52 -050035 {
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050036 reqLevel = static_cast<Entry::Level>(levelmap->second);
Marri Devender Rao7656fba2017-08-06 05:42:52 -050037 }
38
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050039 return reqLevel;
40}
41
42void Manager::commit(uint64_t transactionId, std::string errMsg)
43{
44 auto level = getLevel(errMsg);
45 _commit(transactionId, std::move(errMsg), level);
46}
47
48void Manager::commitWithLvl(uint64_t transactionId, std::string errMsg,
49 uint32_t errLvl)
50{
51 _commit(transactionId, std::move(errMsg),
52 static_cast<Entry::Level>(errLvl));
53}
54
55void Manager::_commit(uint64_t transactionId, std::string&& errMsg,
56 Entry::Level errLvl)
57{
58 if (errLvl < Entry::sevLowerLimit)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050059 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -060060 if (realErrors.size() >= ERROR_CAP)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050061 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -060062 erase(realErrors.front());
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050063 }
64 }
65 else
66 {
67 if (infoErrors.size() >= ERROR_INFO_CAP)
68 {
69 erase(infoErrors.front());
70 }
71 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -060072 constexpr const auto transactionIdVar = "TRANSACTION_ID";
Adriana Kobylak27c87d92017-03-06 12:45:09 -060073 // Length of 'TRANSACTION_ID' string.
Adriana Kobylak67218992017-02-28 12:53:37 -060074 constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
Adriana Kobylak27c87d92017-03-06 12:45:09 -060075 // Length of 'TRANSACTION_ID=' string.
76 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050077
Adriana Kobylakcfd9a7d2017-06-07 11:57:31 -050078 // Flush all the pending log messages into the journal via Synchronize
79 constexpr auto JOURNAL_BUSNAME = "org.freedesktop.journal1";
80 constexpr auto JOURNAL_PATH = "/org/freedesktop/journal1";
81 constexpr auto JOURNAL_INTERFACE = "org.freedesktop.journal1";
82 auto bus = sdbusplus::bus::new_default();
83 auto method = bus.new_method_call(JOURNAL_BUSNAME, JOURNAL_PATH,
84 JOURNAL_INTERFACE, "Synchronize");
85 bus.call_noreply(method);
86
Adriana Kobylakd311bc82016-10-16 09:54:40 -050087 sd_journal *j = nullptr;
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060088 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
Adriana Kobylakd311bc82016-10-16 09:54:40 -050089 if (rc < 0)
90 {
91 logging::log<logging::level::ERR>("Failed to open journal",
92 logging::entry("DESCRIPTION=%s", strerror(-rc)));
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060093 return;
Adriana Kobylakd311bc82016-10-16 09:54:40 -050094 }
95
Adriana Kobylak7298dc22017-01-24 12:21:50 -060096 std::string transactionIdStr = std::to_string(transactionId);
Adriana Kobylakd722b3a2017-02-28 12:10:44 -060097 std::set<std::string> metalist;
98 auto metamap = g_errMetaMap.find(errMsg);
99 if (metamap != g_errMetaMap.end())
100 {
101 metalist.insert(metamap->second.begin(), metamap->second.end());
102 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -0600103
Jayanth Othayothdb18ebe2017-09-04 00:48:02 -0500104 //Add _PID field information in AdditionalData.
105 metalist.insert("_PID");
106
Tom Joseph7a33ee42017-07-25 00:04:20 +0530107 std::vector<std::string> additionalData;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600108
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600109 // Read the journal from the end to get the most recent entry first.
110 // The result from the sd_journal_get_data() is of the form VARIABLE=value.
111 SD_JOURNAL_FOREACH_BACKWARDS(j)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600112 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600113 const char *data = nullptr;
114 size_t length = 0;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600115
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600116 // Look for the transaction id metadata variable
117 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
118 &length);
119 if (rc < 0)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600120 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600121 // This journal entry does not have the TRANSACTION_ID
122 // metadata variable.
123 continue;
124 }
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600125
Adriana Kobylak27c87d92017-03-06 12:45:09 -0600126 // journald does not guarantee that sd_journal_get_data() returns NULL
127 // terminated strings, so need to specify the size to use to compare,
128 // use the returned length instead of anything that relies on NULL
129 // terminators like strlen().
130 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
131 // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
132 // 'data + transactionIdVarOffset' will be in the form of '1234'.
133 // 'length - transactionIdVarOffset' will be the length of '1234'.
134 if ((length <= (transactionIdVarOffset)) ||
135 (transactionIdStr.compare(0,
136 transactionIdStr.size(),
137 data + transactionIdVarOffset,
138 length - transactionIdVarOffset) != 0))
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600139 {
140 // The value of the TRANSACTION_ID metadata is not the requested
141 // transaction id number.
142 continue;
143 }
144
145 // Search for all metadata variables in the current journal entry.
146 for (auto i = metalist.cbegin(); i != metalist.cend();)
147 {
148 rc = sd_journal_get_data(j, (*i).c_str(),
149 (const void **)&data, &length);
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600150 if (rc < 0)
151 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600152 // Metadata variable not found, check next metadata variable.
153 i++;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600154 continue;
155 }
156
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600157 // Metadata variable found, save it and remove it from the set.
158 additionalData.emplace_back(data, length);
159 i = metalist.erase(i);
160 }
161 if (metalist.empty())
162 {
163 // All metadata variables found, break out of journal loop.
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600164 break;
165 }
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600166 }
167 if (!metalist.empty())
168 {
169 // Not all the metadata variables were found in the journal.
170 for (auto& metaVarStr : metalist)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600171 {
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600172 logging::log<logging::level::INFO>("Failed to find metadata",
173 logging::entry("META_FIELD=%s", metaVarStr.c_str()));
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600174 }
175 }
176
Adriana Kobylakd311bc82016-10-16 09:54:40 -0500177 sd_journal_close(j);
178
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600179 // Create error Entry dbus object
180 entryId++;
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -0500181 if (errLvl >= Entry::sevLowerLimit)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500182 {
183 infoErrors.push_back(entryId);
184 }
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600185 else
186 {
187 realErrors.push_back(entryId);
188 }
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600189 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
190 std::chrono::system_clock::now().time_since_epoch()).count();
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600191 auto objPath = std::string(OBJ_ENTRY) + '/' +
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600192 std::to_string(entryId);
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600193
Deepak Kodihalli35b46372017-02-27 04:58:18 -0600194 AssociationList objects {};
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600195 processMetadata(errMsg, additionalData, objects);
196
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500197 auto e = std::make_unique<Entry>(
198 busLog,
199 objPath,
200 entryId,
201 ms, // Milliseconds since 1970
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -0500202 errLvl,
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500203 std::move(errMsg),
204 std::move(additionalData),
205 std::move(objects),
206 *this);
207 serialize(*e);
208 entries.insert(std::make_pair(entryId, std::move(e)));
Adriana Kobylak1db1bd32016-10-10 11:39:20 -0500209}
210
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600211void Manager::processMetadata(const std::string& errorName,
212 const std::vector<std::string>& additionalData,
213 AssociationList& objects) const
214{
215 // additionalData is a list of "metadata=value"
216 constexpr auto separator = '=';
217 for(const auto& entry: additionalData)
218 {
219 auto found = entry.find(separator);
220 if(std::string::npos != found)
221 {
222 auto metadata = entry.substr(0, found);
223 auto iter = meta.find(metadata);
224 if(meta.end() != iter)
225 {
226 (iter->second)(metadata, additionalData, objects);
227 }
228 }
229 }
230}
231
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500232void Manager::erase(uint32_t entryId)
233{
234 auto entry = entries.find(entryId);
235 if(entries.end() != entry)
236 {
Deepak Kodihalli33887992017-06-13 07:06:49 -0500237 // Delete the persistent representation of this error.
238 fs::path errorPath(ERRLOG_PERSIST_PATH);
Marri Devender Rao8959efc2017-11-17 00:13:41 -0600239 errorPath /= std::to_string(entryId);
Deepak Kodihalli33887992017-06-13 07:06:49 -0500240 fs::remove(errorPath);
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600241
242 auto removeId = [](std::list<uint32_t>& ids , uint32_t id)
243 {
244 auto it = std::find(ids.begin(), ids.end(), id);
245 if (it != ids.end())
246 {
247 ids.erase(it);
248 }
249 };
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500250 if (entry->second->severity() >= Entry::sevLowerLimit)
251 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600252 removeId(infoErrors, entryId);
253 }
254 else
255 {
256 removeId(realErrors, entryId);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500257 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500258 entries.erase(entry);
259 }
Marri Devender Rao8959efc2017-11-17 00:13:41 -0600260 else
261 {
262 logging::log<level::ERR>("Invalid entry ID to delete",
263 logging::entry("ID=%d", entryId));
264 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500265}
266
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500267void Manager::restore()
268{
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600269 auto sanity = [](const auto& id, const auto& restoredId)
270 {
271 return id == restoredId;
272 };
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500273 std::vector<uint32_t> errorIds;
274
275 fs::path dir(ERRLOG_PERSIST_PATH);
276 if (!fs::exists(dir) || fs::is_empty(dir))
277 {
278 return;
279 }
280
281 for(auto& file: fs::directory_iterator(dir))
282 {
283 auto id = file.path().filename().c_str();
284 auto idNum = std::stol(id);
285 auto e = std::make_unique<Entry>(
286 busLog,
287 std::string(OBJ_ENTRY) + '/' + id,
288 idNum,
289 *this);
290 if (deserialize(file.path(), *e))
291 {
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600292 //validate the restored error entry id
293 if (sanity(static_cast<uint32_t>(idNum), e->id()))
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500294 {
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600295 e->emit_object_added();
296 if (e->severity() >= Entry::sevLowerLimit)
297 {
298 infoErrors.push_back(idNum);
299 }
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600300 else
301 {
302 realErrors.push_back(idNum);
303 }
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600304
305 entries.insert(std::make_pair(idNum, std::move(e)));
306 errorIds.push_back(idNum);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500307 }
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600308 else
309 {
310 logging::log<logging::level::ERR>(
311 "Failed in sanity check while restoring error entry. "
312 "Ignoring error entry",
313 logging::entry("ID_NUM=%d", idNum),
314 logging::entry("ENTRY_ID=%d", e->id()));
315 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500316 }
317 }
318
Vishwanatha Subbanna37af9ba2017-09-28 16:33:53 +0530319 if (!errorIds.empty())
320 {
321 entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
322 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500323}
324
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -0500325} // namespace internal
Adriana Kobylak8f7941e2016-11-14 14:46:23 -0600326} // namespace logging
327} // namepsace phosphor