blob: 71858f632fa485eac9b1d5365b1fad3c44715eb2 [file] [log] [blame]
Adriana Kobylakd311bc82016-10-16 09:54:40 -05001#include <fstream>
Adriana Kobylak5f4247f2018-03-15 10:27:05 -05002#include <future>
Adriana Kobylakd311bc82016-10-16 09:54:40 -05003#include <iostream>
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -06004#include <chrono>
Adriana Kobylakd311bc82016-10-16 09:54:40 -05005#include <cstdio>
Adriana Kobylak5f4247f2018-03-15 10:27:05 -05006#include <poll.h>
Adriana Kobylakfbe88722017-02-22 16:49:59 -06007#include <set>
Adriana Kobylakd311bc82016-10-16 09:54:40 -05008#include <string>
9#include <vector>
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050010#include <sdbusplus/vtable.hpp>
Adriana Kobylak5f4247f2018-03-15 10:27:05 -050011#include <sys/inotify.h>
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050012#include <systemd/sd-bus.h>
Adriana Kobylakd311bc82016-10-16 09:54:40 -050013#include <systemd/sd-journal.h>
Adriana Kobylak5f4247f2018-03-15 10:27:05 -050014#include <unistd.h>
Adriana Kobylak4ea7f312017-01-10 12:52:34 -060015#include "config.h"
16#include "elog_entry.hpp"
Saqib Khan2bb15192017-02-13 13:19:55 -060017#include <phosphor-logging/log.hpp>
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060018#include "log_manager.hpp"
Deepak Kodihallia87c1572017-02-28 07:40:34 -060019#include "elog_meta.hpp"
Deepak Kodihalli72654f12017-06-12 04:33:29 -050020#include "elog_serialize.hpp"
Deepak Kodihallia87c1572017-02-28 07:40:34 -060021
22using namespace phosphor::logging;
Adriana Kobylak5f4247f2018-03-15 10:27:05 -050023using namespace std::chrono;
Deepak Kodihallia87c1572017-02-28 07:40:34 -060024extern const std::map<metadata::Metadata,
25 std::function<metadata::associations::Type>> meta;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050026
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060027namespace phosphor
28{
29namespace logging
30{
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -050031namespace internal
32{
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050033inline auto getLevel(const std::string& errMsg)
34{
35 auto reqLevel = Entry::Level::Error; // Default to Error
36
37 auto levelmap = g_errLevelMap.find(errMsg);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050038 if (levelmap != g_errLevelMap.end())
Marri Devender Rao7656fba2017-08-06 05:42:52 -050039 {
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050040 reqLevel = static_cast<Entry::Level>(levelmap->second);
Marri Devender Rao7656fba2017-08-06 05:42:52 -050041 }
42
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -050043 return reqLevel;
44}
45
46void Manager::commit(uint64_t transactionId, std::string errMsg)
47{
48 auto level = getLevel(errMsg);
49 _commit(transactionId, std::move(errMsg), level);
50}
51
52void Manager::commitWithLvl(uint64_t transactionId, std::string errMsg,
53 uint32_t errLvl)
54{
55 _commit(transactionId, std::move(errMsg),
56 static_cast<Entry::Level>(errLvl));
57}
58
59void Manager::_commit(uint64_t transactionId, std::string&& errMsg,
60 Entry::Level errLvl)
61{
62 if (errLvl < Entry::sevLowerLimit)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050063 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -060064 if (realErrors.size() >= ERROR_CAP)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050065 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -060066 erase(realErrors.front());
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -050067 }
68 }
69 else
70 {
71 if (infoErrors.size() >= ERROR_INFO_CAP)
72 {
73 erase(infoErrors.front());
74 }
75 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -060076 constexpr const auto transactionIdVar = "TRANSACTION_ID";
Adriana Kobylak27c87d92017-03-06 12:45:09 -060077 // Length of 'TRANSACTION_ID' string.
Adriana Kobylak67218992017-02-28 12:53:37 -060078 constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
Adriana Kobylak27c87d92017-03-06 12:45:09 -060079 // Length of 'TRANSACTION_ID=' string.
80 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
Adriana Kobylak1db1bd32016-10-10 11:39:20 -050081
Adriana Kobylak5f4247f2018-03-15 10:27:05 -050082 // Flush all the pending log messages into the journal
83 journalSync();
Adriana Kobylakcfd9a7d2017-06-07 11:57:31 -050084
Adriana Kobylakd311bc82016-10-16 09:54:40 -050085 sd_journal *j = nullptr;
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060086 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
Adriana Kobylakd311bc82016-10-16 09:54:40 -050087 if (rc < 0)
88 {
89 logging::log<logging::level::ERR>("Failed to open journal",
90 logging::entry("DESCRIPTION=%s", strerror(-rc)));
Adriana Kobylak8f7941e2016-11-14 14:46:23 -060091 return;
Adriana Kobylakd311bc82016-10-16 09:54:40 -050092 }
93
Adriana Kobylak7298dc22017-01-24 12:21:50 -060094 std::string transactionIdStr = std::to_string(transactionId);
Adriana Kobylakd722b3a2017-02-28 12:10:44 -060095 std::set<std::string> metalist;
96 auto metamap = g_errMetaMap.find(errMsg);
97 if (metamap != g_errMetaMap.end())
98 {
99 metalist.insert(metamap->second.begin(), metamap->second.end());
100 }
Adriana Kobylak7298dc22017-01-24 12:21:50 -0600101
Jayanth Othayothdb18ebe2017-09-04 00:48:02 -0500102 //Add _PID field information in AdditionalData.
103 metalist.insert("_PID");
104
Tom Joseph7a33ee42017-07-25 00:04:20 +0530105 std::vector<std::string> additionalData;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600106
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600107 // Read the journal from the end to get the most recent entry first.
108 // The result from the sd_journal_get_data() is of the form VARIABLE=value.
109 SD_JOURNAL_FOREACH_BACKWARDS(j)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600110 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600111 const char *data = nullptr;
112 size_t length = 0;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600113
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600114 // Look for the transaction id metadata variable
115 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
116 &length);
117 if (rc < 0)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600118 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600119 // This journal entry does not have the TRANSACTION_ID
120 // metadata variable.
121 continue;
122 }
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600123
Adriana Kobylak27c87d92017-03-06 12:45:09 -0600124 // journald does not guarantee that sd_journal_get_data() returns NULL
125 // terminated strings, so need to specify the size to use to compare,
126 // use the returned length instead of anything that relies on NULL
127 // terminators like strlen().
128 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
129 // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
130 // 'data + transactionIdVarOffset' will be in the form of '1234'.
131 // 'length - transactionIdVarOffset' will be the length of '1234'.
132 if ((length <= (transactionIdVarOffset)) ||
133 (transactionIdStr.compare(0,
134 transactionIdStr.size(),
135 data + transactionIdVarOffset,
136 length - transactionIdVarOffset) != 0))
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600137 {
138 // The value of the TRANSACTION_ID metadata is not the requested
139 // transaction id number.
140 continue;
141 }
142
143 // Search for all metadata variables in the current journal entry.
144 for (auto i = metalist.cbegin(); i != metalist.cend();)
145 {
146 rc = sd_journal_get_data(j, (*i).c_str(),
147 (const void **)&data, &length);
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600148 if (rc < 0)
149 {
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600150 // Metadata variable not found, check next metadata variable.
151 i++;
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600152 continue;
153 }
154
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600155 // Metadata variable found, save it and remove it from the set.
156 additionalData.emplace_back(data, length);
157 i = metalist.erase(i);
158 }
159 if (metalist.empty())
160 {
161 // All metadata variables found, break out of journal loop.
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600162 break;
163 }
Adriana Kobylakfbe88722017-02-22 16:49:59 -0600164 }
165 if (!metalist.empty())
166 {
167 // Not all the metadata variables were found in the journal.
168 for (auto& metaVarStr : metalist)
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600169 {
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600170 logging::log<logging::level::INFO>("Failed to find metadata",
171 logging::entry("META_FIELD=%s", metaVarStr.c_str()));
Adriana Kobylak9aa7d782017-02-18 09:20:49 -0600172 }
173 }
174
Adriana Kobylakd311bc82016-10-16 09:54:40 -0500175 sd_journal_close(j);
176
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600177 // Create error Entry dbus object
178 entryId++;
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -0500179 if (errLvl >= Entry::sevLowerLimit)
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500180 {
181 infoErrors.push_back(entryId);
182 }
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600183 else
184 {
185 realErrors.push_back(entryId);
186 }
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600187 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
188 std::chrono::system_clock::now().time_since_epoch()).count();
Adriana Kobylak4ea7f312017-01-10 12:52:34 -0600189 auto objPath = std::string(OBJ_ENTRY) + '/' +
Adriana Kobylakc5f0bbd2017-01-22 14:56:04 -0600190 std::to_string(entryId);
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600191
Deepak Kodihalli35b46372017-02-27 04:58:18 -0600192 AssociationList objects {};
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600193 processMetadata(errMsg, additionalData, objects);
194
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500195 auto e = std::make_unique<Entry>(
196 busLog,
197 objPath,
198 entryId,
199 ms, // Milliseconds since 1970
Deepak Kodihalli6fd9dc42018-04-03 02:08:42 -0500200 errLvl,
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500201 std::move(errMsg),
202 std::move(additionalData),
203 std::move(objects),
Matt Spinler375ac9b2018-05-01 15:20:55 -0500204 fwVersion,
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500205 *this);
206 serialize(*e);
207 entries.insert(std::make_pair(entryId, std::move(e)));
Adriana Kobylak1db1bd32016-10-10 11:39:20 -0500208}
209
Deepak Kodihallia87c1572017-02-28 07:40:34 -0600210void Manager::processMetadata(const std::string& errorName,
211 const std::vector<std::string>& additionalData,
212 AssociationList& objects) const
213{
214 // additionalData is a list of "metadata=value"
215 constexpr auto separator = '=';
216 for(const auto& entry: additionalData)
217 {
218 auto found = entry.find(separator);
219 if(std::string::npos != found)
220 {
221 auto metadata = entry.substr(0, found);
222 auto iter = meta.find(metadata);
223 if(meta.end() != iter)
224 {
225 (iter->second)(metadata, additionalData, objects);
226 }
227 }
228 }
229}
230
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500231void Manager::erase(uint32_t entryId)
232{
233 auto entry = entries.find(entryId);
234 if(entries.end() != entry)
235 {
Deepak Kodihalli33887992017-06-13 07:06:49 -0500236 // Delete the persistent representation of this error.
237 fs::path errorPath(ERRLOG_PERSIST_PATH);
Marri Devender Rao8959efc2017-11-17 00:13:41 -0600238 errorPath /= std::to_string(entryId);
Deepak Kodihalli33887992017-06-13 07:06:49 -0500239 fs::remove(errorPath);
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600240
241 auto removeId = [](std::list<uint32_t>& ids , uint32_t id)
242 {
243 auto it = std::find(ids.begin(), ids.end(), id);
244 if (it != ids.end())
245 {
246 ids.erase(it);
247 }
248 };
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500249 if (entry->second->severity() >= Entry::sevLowerLimit)
250 {
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600251 removeId(infoErrors, entryId);
252 }
253 else
254 {
255 removeId(realErrors, entryId);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500256 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500257 entries.erase(entry);
258 }
Marri Devender Rao8959efc2017-11-17 00:13:41 -0600259 else
260 {
261 logging::log<level::ERR>("Invalid entry ID to delete",
262 logging::entry("ID=%d", entryId));
263 }
Deepak Kodihalli99a85492017-03-31 06:01:57 -0500264}
265
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500266void Manager::restore()
267{
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600268 auto sanity = [](const auto& id, const auto& restoredId)
269 {
270 return id == restoredId;
271 };
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500272 std::vector<uint32_t> errorIds;
273
274 fs::path dir(ERRLOG_PERSIST_PATH);
275 if (!fs::exists(dir) || fs::is_empty(dir))
276 {
277 return;
278 }
279
280 for(auto& file: fs::directory_iterator(dir))
281 {
282 auto id = file.path().filename().c_str();
283 auto idNum = std::stol(id);
284 auto e = std::make_unique<Entry>(
285 busLog,
286 std::string(OBJ_ENTRY) + '/' + id,
287 idNum,
288 *this);
289 if (deserialize(file.path(), *e))
290 {
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600291 //validate the restored error entry id
292 if (sanity(static_cast<uint32_t>(idNum), e->id()))
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500293 {
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600294 e->emit_object_added();
295 if (e->severity() >= Entry::sevLowerLimit)
296 {
297 infoErrors.push_back(idNum);
298 }
Nagaraju Gorugantie4b0b772017-11-30 02:12:45 -0600299 else
300 {
301 realErrors.push_back(idNum);
302 }
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600303
304 entries.insert(std::make_pair(idNum, std::move(e)));
305 errorIds.push_back(idNum);
Nagaraju Gorugantif8a5a792017-10-13 08:09:52 -0500306 }
Marri Devender Rao979ed1c2017-11-17 05:06:25 -0600307 else
308 {
309 logging::log<logging::level::ERR>(
310 "Failed in sanity check while restoring error entry. "
311 "Ignoring error entry",
312 logging::entry("ID_NUM=%d", idNum),
313 logging::entry("ENTRY_ID=%d", e->id()));
314 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500315 }
316 }
317
Vishwanatha Subbanna37af9ba2017-09-28 16:33:53 +0530318 if (!errorIds.empty())
319 {
320 entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
321 }
Deepak Kodihalli72654f12017-06-12 04:33:29 -0500322}
323
Adriana Kobylak5f4247f2018-03-15 10:27:05 -0500324void Manager::journalSync()
325{
326 bool syncRequested = false;
327 auto fd = -1;
328 auto rc = -1;
329 auto wd = -1;
330 auto bus = sdbusplus::bus::new_default();
331
332 auto start =
333 duration_cast<microseconds>(steady_clock::now().time_since_epoch())
334 .count();
335
336 constexpr auto maxRetry = 2;
337 for (int i = 0; i < maxRetry; i++)
338 {
339 // Read timestamp from synced file
340 constexpr auto syncedPath = "/run/systemd/journal/synced";
341 std::ifstream syncedFile(syncedPath);
342 if (syncedFile.fail())
343 {
344 log<level::ERR>("Failed to open journal synced file",
345 entry("FILENAME=%s", syncedPath),
346 entry("ERRNO=%d", errno));
347 return;
348 }
349
350 // See if a sync happened by now
351 std::string timestampStr;
352 std::getline(syncedFile, timestampStr);
353 auto timestamp = stoll(timestampStr);
354 if (timestamp >= start)
355 {
356 return;
357 }
358
359 // Let's ask for a sync, but only once
360 if (!syncRequested)
361 {
362 syncRequested = true;
363
364 constexpr auto SYSTEMD_BUSNAME = "org.freedesktop.systemd1";
365 constexpr auto SYSTEMD_PATH = "/org/freedesktop/systemd1";
366 constexpr auto SYSTEMD_INTERFACE =
367 "org.freedesktop.systemd1.Manager";
368 constexpr auto JOURNAL_UNIT = "systemd-journald.service";
369 auto signal = SIGRTMIN + 1;
370
371 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
372 SYSTEMD_INTERFACE, "KillUnit");
373 method.append(JOURNAL_UNIT, "main", signal);
374 bus.call(method);
375 if (method.is_method_error())
376 {
377 log<level::ERR>("Failed to kill journal service");
378 return;
379 }
380 continue;
381 }
382
383 // Let's install the inotify watch, if we didn't do that yet. This watch
384 // monitors the syncedFile for when journald updates it with a newer
385 // timestamp. This means the journal has been flushed.
386 if (fd < 0)
387 {
388 fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
389 if (fd < 0)
390 {
391 log<level::ERR>("Failed to create inotify watch",
392 entry("ERRNO=%d", errno));
393 return;
394 }
395
396 constexpr auto JOURNAL_RUN_PATH = "/run/systemd/journal";
397 wd = inotify_add_watch(fd, JOURNAL_RUN_PATH,
398 IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
399 if (wd < 0)
400 {
401 log<level::ERR>("Failed to watch journal directory",
402 entry("PATH=%s", JOURNAL_RUN_PATH),
403 entry("ERRNO=%d", errno));
404 close(fd);
405 return;
406 }
407 continue;
408 }
409
410 // Let's wait until inotify reports an event
411 struct pollfd fds = {
412 .fd = fd,
413 .events = POLLIN,
414 };
415 constexpr auto pollTimeout = 5; // 5 seconds
416 rc = poll(&fds, 1, pollTimeout * 1000);
417 if (rc < 0)
418 {
419 log<level::ERR>("Failed to add event", entry("ERRNO=%d", errno),
420 entry("ERR=%s", strerror(-rc)));
421 inotify_rm_watch(fd, wd);
422 close(fd);
423 return;
424 }
425 else if (rc == 0)
426 {
427 log<level::INFO>("Poll timeout, no new journal synced data",
428 entry("TIMEOUT=%d", pollTimeout));
429 break;
430 }
431
432 // Read from the specified file descriptor until there is no new data,
433 // throwing away everything read since the timestamp will be read at the
434 // beginning of the loop.
435 constexpr auto maxBytes = 64;
436 uint8_t buffer[maxBytes];
437 while (read(fd, buffer, maxBytes) > 0)
438 ;
439 }
440
Adriana Kobylak4a029f22018-05-23 12:58:19 -0500441 if (fd != -1)
Adriana Kobylak5f4247f2018-03-15 10:27:05 -0500442 {
Adriana Kobylak4a029f22018-05-23 12:58:19 -0500443 if (wd != -1)
444 {
445 inotify_rm_watch(fd, wd);
446 }
Adriana Kobylak5f4247f2018-03-15 10:27:05 -0500447 close(fd);
448 }
449
450 return;
451}
452
Matt Spinler1275bd12018-05-01 15:13:53 -0500453std::string Manager::readFWVersion()
454{
455 std::string version;
456 std::ifstream versionFile{BMC_VERSION_FILE};
457 std::string line;
458 static constexpr auto VERSION_ID = "VERSION_ID=";
459
460 while (std::getline(versionFile, line))
461 {
462 if (line.find(VERSION_ID) != std::string::npos)
463 {
464 auto pos = line.find_first_of('"') + 1;
465 version = line.substr(pos, line.find_last_of('"') - pos);
466 break;
467 }
468 }
469
470 if (version.empty())
471 {
472 log<level::ERR>("Unable to read BMC firmware version");
473 }
474
475 return version;
476}
477
Nagaraju Goruganti05aae8b2017-08-30 07:56:12 -0500478} // namespace internal
Adriana Kobylak8f7941e2016-11-14 14:46:23 -0600479} // namespace logging
480} // namepsace phosphor