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, ×tamp); 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