xref: /openbmc/bmcweb/features/redfish/include/utils/journal_utils.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
160e995cdSEd Tanous #pragma once
260e995cdSEd Tanous 
3*d7857201SEd Tanous #include "bmcweb_config.h"
4*d7857201SEd Tanous 
5*d7857201SEd Tanous #include "generated/enums/log_entry.hpp"
660e995cdSEd Tanous #include "logging.hpp"
7*d7857201SEd Tanous #include "utility.hpp"
860e995cdSEd Tanous #include "utils/time_utils.hpp"
960e995cdSEd Tanous 
1060e995cdSEd Tanous #include <systemd/sd-journal.h>
1160e995cdSEd Tanous 
1260e995cdSEd Tanous #include <nlohmann/json.hpp>
1360e995cdSEd Tanous 
14*d7857201SEd Tanous #include <algorithm>
15*d7857201SEd Tanous #include <charconv>
16*d7857201SEd Tanous #include <cstddef>
17*d7857201SEd Tanous #include <cstdint>
18*d7857201SEd Tanous #include <format>
19*d7857201SEd Tanous #include <memory>
2060e995cdSEd Tanous #include <string>
2160e995cdSEd Tanous #include <string_view>
22*d7857201SEd Tanous #include <utility>
2360e995cdSEd Tanous 
2460e995cdSEd Tanous namespace redfish
2560e995cdSEd Tanous {
2660e995cdSEd Tanous 
2760e995cdSEd Tanous inline int getJournalMetadata(sd_journal* journal, const char* field,
2860e995cdSEd Tanous                               std::string_view& contents)
2960e995cdSEd Tanous {
3060e995cdSEd Tanous     const char* data = nullptr;
3160e995cdSEd Tanous     size_t length = 0;
3260e995cdSEd Tanous     int ret = 0;
3360e995cdSEd Tanous     // Get the metadata from the requested field of the journal entry
3460e995cdSEd Tanous     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3560e995cdSEd Tanous     const void** dataVoid = reinterpret_cast<const void**>(&data);
3660e995cdSEd Tanous 
3760e995cdSEd Tanous     ret = sd_journal_get_data(journal, field, dataVoid, &length);
3860e995cdSEd Tanous     if (ret < 0)
3960e995cdSEd Tanous     {
4060e995cdSEd Tanous         return ret;
4160e995cdSEd Tanous     }
4260e995cdSEd Tanous     contents = std::string_view(data, length);
4360e995cdSEd Tanous     // Only use the content after the "=" character.
4460e995cdSEd Tanous     contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
4560e995cdSEd Tanous     return ret;
4660e995cdSEd Tanous }
4760e995cdSEd Tanous 
4860e995cdSEd Tanous inline int getJournalMetadataInt(sd_journal* journal, const char* field,
4960e995cdSEd Tanous                                  long int& contents)
5060e995cdSEd Tanous {
5160e995cdSEd Tanous     std::string_view metadata;
5260e995cdSEd Tanous     // Get the metadata from the requested field of the journal entry
5360e995cdSEd Tanous     int ret = getJournalMetadata(journal, field, metadata);
5460e995cdSEd Tanous     if (ret < 0)
5560e995cdSEd Tanous     {
5660e995cdSEd Tanous         return ret;
5760e995cdSEd Tanous     }
5860e995cdSEd Tanous     std::from_chars_result res =
5960e995cdSEd Tanous         std::from_chars(&*metadata.begin(), &*metadata.end(), contents);
6060e995cdSEd Tanous     if (res.ec != std::error_code{} || res.ptr != &*metadata.end())
6160e995cdSEd Tanous     {
6260e995cdSEd Tanous         return -1;
6360e995cdSEd Tanous     }
6460e995cdSEd Tanous     return 0;
6560e995cdSEd Tanous }
6660e995cdSEd Tanous 
6760e995cdSEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
6860e995cdSEd Tanous {
6960e995cdSEd Tanous     int ret = 0;
7060e995cdSEd Tanous     uint64_t timestamp = 0;
7160e995cdSEd Tanous     ret = sd_journal_get_realtime_usec(journal, &timestamp);
7260e995cdSEd Tanous     if (ret < 0)
7360e995cdSEd Tanous     {
7460e995cdSEd Tanous         BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", ret);
7560e995cdSEd Tanous         return false;
7660e995cdSEd Tanous     }
7760e995cdSEd Tanous     entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp);
7860e995cdSEd Tanous     return true;
7960e995cdSEd Tanous }
8060e995cdSEd Tanous 
8160e995cdSEd Tanous inline bool fillBMCJournalLogEntryJson(
8260e995cdSEd Tanous     sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson)
8360e995cdSEd Tanous {
8460e995cdSEd Tanous     char* cursor = nullptr;
8560e995cdSEd Tanous     int ret = sd_journal_get_cursor(journal, &cursor);
8660e995cdSEd Tanous     if (ret < 0)
8760e995cdSEd Tanous     {
8860e995cdSEd Tanous         return false;
8960e995cdSEd Tanous     }
9060e995cdSEd Tanous     std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor);
9160e995cdSEd Tanous     std::string bmcJournalLogEntryID(cursor);
9260e995cdSEd Tanous 
9360e995cdSEd Tanous     // Get the Log Entry contents
9460e995cdSEd Tanous     std::string message;
9560e995cdSEd Tanous     std::string_view syslogID;
9660e995cdSEd Tanous     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
9760e995cdSEd Tanous     if (ret < 0)
9860e995cdSEd Tanous     {
9960e995cdSEd Tanous         BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", ret);
10060e995cdSEd Tanous     }
10160e995cdSEd Tanous     if (!syslogID.empty())
10260e995cdSEd Tanous     {
10360e995cdSEd Tanous         message += std::string(syslogID) + ": ";
10460e995cdSEd Tanous     }
10560e995cdSEd Tanous 
10660e995cdSEd Tanous     std::string_view msg;
10760e995cdSEd Tanous     ret = getJournalMetadata(journal, "MESSAGE", msg);
10860e995cdSEd Tanous     if (ret < 0)
10960e995cdSEd Tanous     {
11060e995cdSEd Tanous         BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", ret);
11160e995cdSEd Tanous         return false;
11260e995cdSEd Tanous     }
11360e995cdSEd Tanous     message += std::string(msg);
11460e995cdSEd Tanous 
11560e995cdSEd Tanous     // Get the severity from the PRIORITY field
11660e995cdSEd Tanous     long int severity = 8; // Default to an invalid priority
11760e995cdSEd Tanous     ret = getJournalMetadataInt(journal, "PRIORITY", severity);
11860e995cdSEd Tanous     if (ret < 0)
11960e995cdSEd Tanous     {
12060e995cdSEd Tanous         BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", ret);
12160e995cdSEd Tanous     }
12260e995cdSEd Tanous 
12360e995cdSEd Tanous     // Get the Created time from the timestamp
12460e995cdSEd Tanous     std::string entryTimeStr;
12560e995cdSEd Tanous     if (!getEntryTimestamp(journal, entryTimeStr))
12660e995cdSEd Tanous     {
12760e995cdSEd Tanous         return false;
12860e995cdSEd Tanous     }
12960e995cdSEd Tanous 
13060e995cdSEd Tanous     // Fill in the log entry with the gathered data
13160e995cdSEd Tanous     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
13260e995cdSEd Tanous 
13360e995cdSEd Tanous     std::string entryIdBase64 =
13460e995cdSEd Tanous         crow::utility::base64encode(bmcJournalLogEntryID);
13560e995cdSEd Tanous 
136*d7857201SEd Tanous     bmcJournalLogEntryJson["@odata.id"] = boost_swap_impl::format(
13760e995cdSEd Tanous         "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}",
13860e995cdSEd Tanous         BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64);
13960e995cdSEd Tanous     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
14060e995cdSEd Tanous     bmcJournalLogEntryJson["Id"] = entryIdBase64;
14160e995cdSEd Tanous     bmcJournalLogEntryJson["Message"] = std::move(message);
14260e995cdSEd Tanous     bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem;
143*d7857201SEd Tanous     log_entry::EventSeverity rfseverity = log_entry::EventSeverity::OK;
14460e995cdSEd Tanous     if (severity <= 2)
14560e995cdSEd Tanous     {
146*d7857201SEd Tanous         rfseverity = log_entry::EventSeverity::Critical;
14760e995cdSEd Tanous     }
14860e995cdSEd Tanous     else if (severity <= 4)
14960e995cdSEd Tanous     {
150*d7857201SEd Tanous         rfseverity = log_entry::EventSeverity::Warning;
15160e995cdSEd Tanous     }
15260e995cdSEd Tanous 
153*d7857201SEd Tanous     bmcJournalLogEntryJson["Severity"] = rfseverity;
15460e995cdSEd Tanous     bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
15560e995cdSEd Tanous     bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
15660e995cdSEd Tanous     return true;
15760e995cdSEd Tanous }
15860e995cdSEd Tanous 
15960e995cdSEd Tanous } // namespace redfish
160