xref: /openbmc/bmcweb/features/redfish/include/utils/journal_utils.hpp (revision 60e995cd6ab15aac32e146860c2a99c069ea111f)
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, &timestamp);
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