1*60e995cdSEd Tanous #pragma once 2*60e995cdSEd Tanous 3*60e995cdSEd Tanous #include "logging.hpp" 4*60e995cdSEd Tanous #include "utils/time_utils.hpp" 5*60e995cdSEd Tanous 6*60e995cdSEd Tanous #include <systemd/sd-journal.h> 7*60e995cdSEd Tanous 8*60e995cdSEd Tanous #include <nlohmann/json.hpp> 9*60e995cdSEd Tanous 10*60e995cdSEd Tanous #include <string> 11*60e995cdSEd Tanous #include <string_view> 12*60e995cdSEd Tanous 13*60e995cdSEd Tanous namespace redfish 14*60e995cdSEd Tanous { 15*60e995cdSEd Tanous 16*60e995cdSEd Tanous inline int getJournalMetadata(sd_journal* journal, const char* field, 17*60e995cdSEd Tanous std::string_view& contents) 18*60e995cdSEd Tanous { 19*60e995cdSEd Tanous const char* data = nullptr; 20*60e995cdSEd Tanous size_t length = 0; 21*60e995cdSEd Tanous int ret = 0; 22*60e995cdSEd Tanous // Get the metadata from the requested field of the journal entry 23*60e995cdSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 24*60e995cdSEd Tanous const void** dataVoid = reinterpret_cast<const void**>(&data); 25*60e995cdSEd Tanous 26*60e995cdSEd Tanous ret = sd_journal_get_data(journal, field, dataVoid, &length); 27*60e995cdSEd Tanous if (ret < 0) 28*60e995cdSEd Tanous { 29*60e995cdSEd Tanous return ret; 30*60e995cdSEd Tanous } 31*60e995cdSEd Tanous contents = std::string_view(data, length); 32*60e995cdSEd Tanous // Only use the content after the "=" character. 33*60e995cdSEd Tanous contents.remove_prefix(std::min(contents.find('=') + 1, contents.size())); 34*60e995cdSEd Tanous return ret; 35*60e995cdSEd Tanous } 36*60e995cdSEd Tanous 37*60e995cdSEd Tanous inline int getJournalMetadataInt(sd_journal* journal, const char* field, 38*60e995cdSEd Tanous long int& contents) 39*60e995cdSEd Tanous { 40*60e995cdSEd Tanous std::string_view metadata; 41*60e995cdSEd Tanous // Get the metadata from the requested field of the journal entry 42*60e995cdSEd Tanous int ret = getJournalMetadata(journal, field, metadata); 43*60e995cdSEd Tanous if (ret < 0) 44*60e995cdSEd Tanous { 45*60e995cdSEd Tanous return ret; 46*60e995cdSEd Tanous } 47*60e995cdSEd Tanous std::from_chars_result res = 48*60e995cdSEd Tanous std::from_chars(&*metadata.begin(), &*metadata.end(), contents); 49*60e995cdSEd Tanous if (res.ec != std::error_code{} || res.ptr != &*metadata.end()) 50*60e995cdSEd Tanous { 51*60e995cdSEd Tanous return -1; 52*60e995cdSEd Tanous } 53*60e995cdSEd Tanous return 0; 54*60e995cdSEd Tanous } 55*60e995cdSEd Tanous 56*60e995cdSEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp) 57*60e995cdSEd Tanous { 58*60e995cdSEd Tanous int ret = 0; 59*60e995cdSEd Tanous uint64_t timestamp = 0; 60*60e995cdSEd Tanous ret = sd_journal_get_realtime_usec(journal, ×tamp); 61*60e995cdSEd Tanous if (ret < 0) 62*60e995cdSEd Tanous { 63*60e995cdSEd Tanous BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", ret); 64*60e995cdSEd Tanous return false; 65*60e995cdSEd Tanous } 66*60e995cdSEd Tanous entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp); 67*60e995cdSEd Tanous return true; 68*60e995cdSEd Tanous } 69*60e995cdSEd Tanous 70*60e995cdSEd Tanous inline bool fillBMCJournalLogEntryJson( 71*60e995cdSEd Tanous sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson) 72*60e995cdSEd Tanous { 73*60e995cdSEd Tanous char* cursor = nullptr; 74*60e995cdSEd Tanous int ret = sd_journal_get_cursor(journal, &cursor); 75*60e995cdSEd Tanous if (ret < 0) 76*60e995cdSEd Tanous { 77*60e995cdSEd Tanous return false; 78*60e995cdSEd Tanous } 79*60e995cdSEd Tanous std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor); 80*60e995cdSEd Tanous std::string bmcJournalLogEntryID(cursor); 81*60e995cdSEd Tanous 82*60e995cdSEd Tanous // Get the Log Entry contents 83*60e995cdSEd Tanous std::string message; 84*60e995cdSEd Tanous std::string_view syslogID; 85*60e995cdSEd Tanous ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID); 86*60e995cdSEd Tanous if (ret < 0) 87*60e995cdSEd Tanous { 88*60e995cdSEd Tanous BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", ret); 89*60e995cdSEd Tanous } 90*60e995cdSEd Tanous if (!syslogID.empty()) 91*60e995cdSEd Tanous { 92*60e995cdSEd Tanous message += std::string(syslogID) + ": "; 93*60e995cdSEd Tanous } 94*60e995cdSEd Tanous 95*60e995cdSEd Tanous std::string_view msg; 96*60e995cdSEd Tanous ret = getJournalMetadata(journal, "MESSAGE", msg); 97*60e995cdSEd Tanous if (ret < 0) 98*60e995cdSEd Tanous { 99*60e995cdSEd Tanous BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", ret); 100*60e995cdSEd Tanous return false; 101*60e995cdSEd Tanous } 102*60e995cdSEd Tanous message += std::string(msg); 103*60e995cdSEd Tanous 104*60e995cdSEd Tanous // Get the severity from the PRIORITY field 105*60e995cdSEd Tanous long int severity = 8; // Default to an invalid priority 106*60e995cdSEd Tanous ret = getJournalMetadataInt(journal, "PRIORITY", severity); 107*60e995cdSEd Tanous if (ret < 0) 108*60e995cdSEd Tanous { 109*60e995cdSEd Tanous BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", ret); 110*60e995cdSEd Tanous } 111*60e995cdSEd Tanous 112*60e995cdSEd Tanous // Get the Created time from the timestamp 113*60e995cdSEd Tanous std::string entryTimeStr; 114*60e995cdSEd Tanous if (!getEntryTimestamp(journal, entryTimeStr)) 115*60e995cdSEd Tanous { 116*60e995cdSEd Tanous return false; 117*60e995cdSEd Tanous } 118*60e995cdSEd Tanous 119*60e995cdSEd Tanous // Fill in the log entry with the gathered data 120*60e995cdSEd Tanous bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 121*60e995cdSEd Tanous 122*60e995cdSEd Tanous std::string entryIdBase64 = 123*60e995cdSEd Tanous crow::utility::base64encode(bmcJournalLogEntryID); 124*60e995cdSEd Tanous 125*60e995cdSEd Tanous bmcJournalLogEntryJson["@odata.id"] = boost::urls::format( 126*60e995cdSEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}", 127*60e995cdSEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64); 128*60e995cdSEd Tanous bmcJournalLogEntryJson["Name"] = "BMC Journal Entry"; 129*60e995cdSEd Tanous bmcJournalLogEntryJson["Id"] = entryIdBase64; 130*60e995cdSEd Tanous bmcJournalLogEntryJson["Message"] = std::move(message); 131*60e995cdSEd Tanous bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem; 132*60e995cdSEd Tanous log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK; 133*60e995cdSEd Tanous if (severity <= 2) 134*60e995cdSEd Tanous { 135*60e995cdSEd Tanous severityEnum = log_entry::EventSeverity::Critical; 136*60e995cdSEd Tanous } 137*60e995cdSEd Tanous else if (severity <= 4) 138*60e995cdSEd Tanous { 139*60e995cdSEd Tanous severityEnum = log_entry::EventSeverity::Warning; 140*60e995cdSEd Tanous } 141*60e995cdSEd Tanous 142*60e995cdSEd Tanous bmcJournalLogEntryJson["Severity"] = severityEnum; 143*60e995cdSEd Tanous bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry"; 144*60e995cdSEd Tanous bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr); 145*60e995cdSEd Tanous return true; 146*60e995cdSEd Tanous } 147*60e995cdSEd Tanous 148*60e995cdSEd Tanous } // namespace redfish 149