blob: c1fe383c52a84e7e7ce3fc2f0dbfa7a3de0bb4d5 [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{
Marri Devender Rao7656fba2017-08-06 05:42:52 -050030 if (capped)
31 {
32 return;
33 }
34 if (entries.size() >= ERROR_CAP)
35 {
36 log<level::ERR>("Reached error cap, Ignoring error",
37 entry("SIZE=%d", entries.size()),
38 entry("ERROR_CAP=%d", ERROR_CAP));
39 capped = true;
40 return;
41 }
42
Adriana Kobylak7298dc22017-01-24 12:21:50 -060043 constexpr const auto transactionIdVar = "TRANSACTION_ID";
Adriana Kobylak27c87d92017-03-06 12:45:09 -060044 // Length of 'TRANSACTION_ID' string.
Adriana Kobylak67218992017-02-28 12:53:37 -060045 constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
Adriana Kobylak27c87d92017-03-06 12:45:09 -060046 // Length of 'TRANSACTION_ID=' string.
47 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050048
Adriana Kobylakcfd9a7d2017-06-07 11:57:31 -050049 // Flush all the pending log messages into the journal via Synchronize
50 constexpr auto JOURNAL_BUSNAME = "org.freedesktop.journal1";
51 constexpr auto JOURNAL_PATH = "/org/freedesktop/journal1";
52 constexpr auto JOURNAL_INTERFACE = "org.freedesktop.journal1";
53 auto bus = sdbusplus::bus::new_default();
54 auto method = bus.new_method_call(JOURNAL_BUSNAME, JOURNAL_PATH,
55 JOURNAL_INTERFACE, "Synchronize");
56 bus.call_noreply(method);
57
Adriana Kobylakd311bc82016-10-16 09:54:40 -050058 sd_journal *j = nullptr;
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060059 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
Adriana Kobylakd311bc82016-10-16 09:54:40 -050060 if (rc < 0)
61 {
62 logging::log<logging::level::ERR>("Failed to open journal",
63 logging::entry("DESCRIPTION=%s", strerror(-rc)));
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060064 return;
Adriana Kobylakd311bc82016-10-16 09:54:40 -050065 }
66
Adriana Kobylak7298dc22017-01-24 12:21:50 -060067 std::string transactionIdStr = std::to_string(transactionId);
Adriana Kobylakd722b3a2017-02-28 12:10:44 -060068 std::set<std::string> metalist;
69 auto metamap = g_errMetaMap.find(errMsg);
70 if (metamap != g_errMetaMap.end())
71 {
72 metalist.insert(metamap->second.begin(), metamap->second.end());
73 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -060074
Jayanth Othayothdb18ebe2017-09-04 00:48:02 -050075 //Add _PID field information in AdditionalData.
76 metalist.insert("_PID");
77
Tom Joseph7a33ee42017-07-25 00:04:20 +053078 std::vector<std::string> additionalData;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060079
Adriana Kobylakfbe88722017-02-22 16:49:59 -060080 // Read the journal from the end to get the most recent entry first.
81 // The result from the sd_journal_get_data() is of the form VARIABLE=value.
82 SD_JOURNAL_FOREACH_BACKWARDS(j)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060083 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -060084 const char *data = nullptr;
85 size_t length = 0;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060086
Adriana Kobylakfbe88722017-02-22 16:49:59 -060087 // Look for the transaction id metadata variable
88 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
89 &length);
90 if (rc < 0)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060091 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -060092 // This journal entry does not have the TRANSACTION_ID
93 // metadata variable.
94 continue;
95 }
Adriana Kobylak9aa7d782017-02-18 09:20:49 -060096
Adriana Kobylak27c87d92017-03-06 12:45:09 -060097 // journald does not guarantee that sd_journal_get_data() returns NULL
98 // terminated strings, so need to specify the size to use to compare,
99 // use the returned length instead of anything that relies on NULL
100 // terminators like strlen().
101 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
102 // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
103 // 'data + transactionIdVarOffset' will be in the form of '1234'.
104 // 'length - transactionIdVarOffset' will be the length of '1234'.
105 if ((length <= (transactionIdVarOffset)) ||
106 (transactionIdStr.compare(0,
107 transactionIdStr.size(),
108 data + transactionIdVarOffset,
109 length - transactionIdVarOffset) != 0))
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600110 {
111 // The value of the TRANSACTION_ID metadata is not the requested
112 // transaction id number.
113 continue;
114 }
115
116 // Search for all metadata variables in the current journal entry.
117 for (auto i = metalist.cbegin(); i != metalist.cend();)
118 {
119 rc = sd_journal_get_data(j, (*i).c_str(),
120 (const void **)&data, &length);
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600121 if (rc < 0)
122 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600123 // Metadata variable not found, check next metadata variable.
124 i++;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600125 continue;
126 }
127
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600128 // Metadata variable found, save it and remove it from the set.
129 additionalData.emplace_back(data, length);
130 i = metalist.erase(i);
131 }
132 if (metalist.empty())
133 {
134 // All metadata variables found, break out of journal loop.
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600135 break;
136 }
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600137 }
138 if (!metalist.empty())
139 {
140 // Not all the metadata variables were found in the journal.
141 for (auto& metaVarStr : metalist)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600142 {
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600143 logging::log<logging::level::INFO>("Failed to find metadata",
144 logging::entry("META_FIELD=%s", metaVarStr.c_str()));
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600145 }
146 }
147
Adriana Kobylakd311bc82016-10-16 09:54:40 -0500148 sd_journal_close(j);
149
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600150 // Create error Entry dbus object
151 entryId++;
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600152 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
153 std::chrono::system_clock::now().time_since_epoch()).count();
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600154 auto objPath = std::string(OBJ_ENTRY) + '/' +
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600155 std::to_string(entryId);
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600156
Deepak Kodihalli35b46372017-02-27 04:58:18 -0600157 AssociationList objects {};
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600158 processMetadata(errMsg, additionalData, objects);
159
Marri Devender Rao0a71bad2017-07-12 08:01:39 -0500160 level reqLevel = level::ERR; // Default to ERR
Adriana Kobylakd722b3a2017-02-28 12:10:44 -0600161 auto levelmap = g_errLevelMap.find(errMsg);
162 if (levelmap != g_errLevelMap.end())
163 {
164 reqLevel = levelmap->second;
165 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500166 auto e = std::make_unique<Entry>(
167 busLog,
168 objPath,
169 entryId,
170 ms, // Milliseconds since 1970
171 static_cast<Entry::Level>(reqLevel),
172 std::move(errMsg),
173 std::move(additionalData),
174 std::move(objects),
175 *this);
176 serialize(*e);
177 entries.insert(std::make_pair(entryId, std::move(e)));
Adriana Kobylak1db1bd32016-10-10 11:39:20 -0500178}
179
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600180void Manager::processMetadata(const std::string& errorName,
181 const std::vector<std::string>& additionalData,
182 AssociationList& objects) const
183{
184 // additionalData is a list of "metadata=value"
185 constexpr auto separator = '=';
186 for(const auto& entry: additionalData)
187 {
188 auto found = entry.find(separator);
189 if(std::string::npos != found)
190 {
191 auto metadata = entry.substr(0, found);
192 auto iter = meta.find(metadata);
193 if(meta.end() != iter)
194 {
195 (iter->second)(metadata, additionalData, objects);
196 }
197 }
198 }
199}
200
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500201void Manager::erase(uint32_t entryId)
202{
203 auto entry = entries.find(entryId);
Deepak Kodihalli33887992017-06-13 07:06:49 -0500204 auto id = entry->second->id();
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500205 if(entries.end() != entry)
206 {
Deepak Kodihalli33887992017-06-13 07:06:49 -0500207 // Delete the persistent representation of this error.
208 fs::path errorPath(ERRLOG_PERSIST_PATH);
209 errorPath /= std::to_string(id);
210 fs::remove(errorPath);
211
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500212 entries.erase(entry);
213 }
Marri Devender Rao7656fba2017-08-06 05:42:52 -0500214
215 if (entries.size() < ERROR_CAP)
216 {
217 capped = false;
218 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500219}
220
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500221void Manager::restore()
222{
223 std::vector<uint32_t> errorIds;
224
225 fs::path dir(ERRLOG_PERSIST_PATH);
226 if (!fs::exists(dir) || fs::is_empty(dir))
227 {
228 return;
229 }
230
231 for(auto& file: fs::directory_iterator(dir))
232 {
233 auto id = file.path().filename().c_str();
234 auto idNum = std::stol(id);
235 auto e = std::make_unique<Entry>(
236 busLog,
237 std::string(OBJ_ENTRY) + '/' + id,
238 idNum,
239 *this);
240 if (deserialize(file.path(), *e))
241 {
242 e->emit_object_added();
243 entries.insert(std::make_pair(idNum, std::move(e)));
244 errorIds.push_back(idNum);
245 }
246 }
247
248 entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
249}
250
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -0500251} // namespace internal
Adriana Kobylak8f7941e2016-11-14 14:46:23 -0600252} // namespace logging
253} // namepsace phosphor